- {username}
+
+ {username}
+
diff --git a/components/oracles-actions.tsx b/components/oracles-actions.tsx
index f8709537bf..28ec57ab39 100644
--- a/components/oracles-actions.tsx
+++ b/components/oracles-actions.tsx
@@ -31,7 +31,7 @@ interface OraclesActionsProps {
}
function OraclesActions({
- wallet,
+ wallet,
updateWalletBalance
} : OraclesActionsProps) {
const { t } = useTranslation(["common", "my-oracles"]);
@@ -57,6 +57,10 @@ function OraclesActions({
const networkTokenSymbol = networkTokenERC20.symbol || t("misc.$token");
const networkTokenDecimals = networkTokenERC20.decimals || 18;
+ const oracleExchangeRate = Service?.network?.amounts?.oracleExchangeRate || 1;
+ const oracleAmount = action === t("my-oracles:actions.lock.label") ?
+ BigNumber(tokenAmount || 0).multipliedBy(oracleExchangeRate).toFixed() :
+ BigNumber(tokenAmount || 0).dividedBy(oracleExchangeRate).toFixed();
const exceedsAvailable = value => BigNumber(value).gt(getMaxAmount());
@@ -74,7 +78,7 @@ function OraclesActions({
token: Service?.network?.networkToken?.symbol
}),
label: t("my-oracles:actions.lock.get-amount-oracles", {
- amount: formatNumberToNScale(tokenAmount),
+ amount: formatNumberToNScale(oracleAmount),
token: Service?.network?.networkToken?.symbol
}),
caption: (
@@ -89,7 +93,8 @@ function OraclesActions({
),
body:
t("my-oracles:actions.lock.body", {
- amount: formatNumberToNScale(tokenAmount),
+ amount: formatNumberToNScale(tokenAmount),
+ oracleAmount: formatNumberToNScale(oracleAmount),
currency: networkTokenSymbol,
token: Service?.network?.networkToken?.symbol
}),
@@ -106,7 +111,7 @@ function OraclesActions({
token: Service?.network?.networkToken?.symbol
}),
label: t("my-oracles:actions.unlock.get-amount-bepro", {
- amount: formatNumberToNScale(tokenAmount),
+ amount: formatNumberToNScale(oracleAmount),
currency: networkTokenSymbol,
token: Service?.network?.networkToken?.symbol
}),
@@ -121,6 +126,7 @@ function OraclesActions({
),
body: t("my-oracles:actions.unlock.body", {
amount: formatNumberToNScale(tokenAmount),
+ oracleAmount: formatNumberToNScale(oracleAmount),
currency: networkTokenSymbol,
token: Service?.network?.networkToken?.symbol
}),
diff --git a/components/page-actions.tsx b/components/page-actions.tsx
index f679cbeb39..f6ddbb15c6 100644
--- a/components/page-actions.tsx
+++ b/components/page-actions.tsx
@@ -2,6 +2,7 @@ import React, {useEffect, useState} from "react";
import {isMobile} from "react-device-detect";
import {useTranslation} from "next-i18next";
+import Link from "next/link";
import {useRouter} from "next/router";
import EditIcon from "assets/icons/transactions/edit";
@@ -26,8 +27,9 @@ import useBepro from "x-hooks/use-bepro";
import {BountyEffectsProvider} from "../contexts/bounty-effects";
import {useBounty} from "../x-hooks/use-bounty";
+import useNetworkTheme from "../x-hooks/use-network-theme";
import ConnectGithub from "./connect-github";
-import { ContextualSpan } from "./contextual-span";
+import {ContextualSpan} from "./contextual-span";
import Modal from "./modal";
interface PageActionsProps {
@@ -66,6 +68,7 @@ export default function PageActions({
const isBountyInDraft = !!state.currentBounty?.chainData?.isDraft;
const isBountyFinished = !!state.currentBounty?.chainData?.isFinished;
const isWalletConnected = !!state.currentUser?.walletAddress;
+ const isKycVerified = state?.currentUser?.kycSession?.status === 'VERIFIED';
const isGithubConnected = !!state.currentUser?.login;
const isFundingRequest =
state.currentBounty?.chainData?.fundingAmount?.gt(0) || state.currentBounty?.data?.fundingAmount?.gt(0);
@@ -237,8 +240,7 @@ export default function PageActions({
+ color="primary">
);
}
@@ -250,22 +252,35 @@ export default function PageActions({
isBountyOpen &&
!isWorkingOnBounty &&
isRepoForked &&
- isStateToWorking)
- return (
-
-
- );
+
+ }
+ else{
+ return (
+
+
+
+
+
+
+
+ );
+ }
+ }
}
function renderCreatePullRequestButton() {
diff --git a/components/profile/my-network-settings/governance-settings.tsx b/components/profile/my-network-settings/governance-settings.tsx
index d91414c186..d0ed300ac5 100644
--- a/components/profile/my-network-settings/governance-settings.tsx
+++ b/components/profile/my-network-settings/governance-settings.tsx
@@ -91,13 +91,12 @@ export default function GovernanceSettings({
}
useEffect(() => {
- console.log('tokens', tokens)
if(tokens.length > 0) setNetworkToken(tokens.map((token) => ({
...token,
isReward: !!token.network_tokens.isReward,
isTransactional: !!token.network_tokens.isTransactional
})))
- }, [tokens])
+ }, [tokens]);
return (
<>
@@ -122,16 +121,10 @@ export default function GovernanceSettings({
disabled={!isAbleToClosed || isClosing || !state.currentUser?.login}
className="ml-2"
onClick={handleCloseMyNetwork}
+ isLoading={isClosing}
+ withLockIcon={!isAbleToClosed || !state.currentUser?.login}
>
- {(!isAbleToClosed || !state.currentUser?.login) && (
-
- )}
{t("custom-network:close-network")}
- {isClosing ? (
-
- ) : (
- ""
- )}
diff --git a/components/profile/my-network-settings/index.tsx b/components/profile/my-network-settings/index.tsx
index b71cc18c4d..f7dc4bc5fd 100644
--- a/components/profile/my-network-settings/index.tsx
+++ b/components/profile/my-network-settings/index.tsx
@@ -4,6 +4,12 @@ import { Col, Row } from "react-bootstrap";
import { useTranslation } from "next-i18next";
import Button from "components/button";
+import { ContainerTab } from "components/profile/my-network-settings/container-tab";
+import GovernanceSettings from "components/profile/my-network-settings/governance-settings";
+import LogoAndColoursSettings from "components/profile/my-network-settings/logo-and-colours-settings";
+import RegistrySettings from "components/profile/my-network-settings/registry-settings";
+import RepositoriesListSettings from "components/profile/my-network-settings/repositories-list-settings";
+import WarningGithub from "components/profile/my-network-settings/warning-github";
import ReadOnlyButtonWrapper from "components/read-only-button-wrapper";
import TabbedNavigation from "components/tabbed-navigation";
@@ -23,13 +29,6 @@ import useBepro from "x-hooks/use-bepro";
import { useNetwork } from "x-hooks/use-network";
import useNetworkTheme from "x-hooks/use-network-theme";
-import { ContainerTab } from "./container-tab";
-import GovernanceSettings from "./governance-settings";
-import LogoAndColoursSettings from "./logo-and-colours-settings";
-import RegistrySettings from "./registry-settings";
-import RepositoriesListSettings from "./repositories-list-settings";
-import WarningGithub from "./warning-github";
-
interface MyNetworkSettingsProps {
network: Network;
updateEditingNetwork: () => void;
@@ -53,12 +52,10 @@ export default function MyNetworkSettings({
const [tabs, setTabs] = useState([])
const { state, dispatch } = useAppState();
-
const { colorsToCSS } = useNetworkTheme();
+ const { updateActiveNetwork } = useNetwork();
const { updateNetwork, processEvent } = useApi();
const { handleChangeNetworkParameter } = useBepro();
-
- const { updateActiveNetwork } = useNetwork();
const {
details,
github,
@@ -130,40 +127,40 @@ export default function MyNetworkSettings({
disputableTime: { value: disputableTime },
councilAmount: { value: councilAmount },
percentageNeededForDispute: { value: percentageForDispute },
+ oracleExchangeRate: { value: oracleExchangeRate },
+ mergeCreatorFeeShare: { value: mergeCreatorFeeShare },
+ proposerFeeShare: { value: proposerFeeShare },
+ cancelableTime: { value: cancelableTime }
},
} = settings;
const networkAddress = network?.networkAddress;
const promises = await Promise.allSettled([
- ...(draftTime !== forcedNetwork.draftTime
- ? [
- handleChangeNetworkParameter("draftTime",
- draftTime,
- networkAddress),
- ]
- : []),
- ...(disputableTime !== forcedNetwork.disputableTime
- ? [
- handleChangeNetworkParameter("disputableTime",
- disputableTime,
- networkAddress),
- ]
- : []),
- ...(councilAmount !== +forcedNetwork.councilAmount
- ? [
- handleChangeNetworkParameter("councilAmount",
- councilAmount,
- networkAddress),
- ]
- : []),
- ...(percentageForDispute !== forcedNetwork.percentageNeededForDispute
- ? [
- handleChangeNetworkParameter("percentageNeededForDispute",
- percentageForDispute,
- networkAddress),
- ]
- : []),
+ ...(draftTime !== forcedNetwork.draftTime ? [
+ handleChangeNetworkParameter("draftTime", draftTime, networkAddress)
+ ] : []),
+ ...(disputableTime !== forcedNetwork.disputableTime ? [
+ handleChangeNetworkParameter("disputableTime", disputableTime, networkAddress)
+ ] : []),
+ ...(councilAmount !== +forcedNetwork.councilAmount ? [
+ handleChangeNetworkParameter("councilAmount", councilAmount, networkAddress)
+ ] : []),
+ ...(percentageForDispute !== forcedNetwork.percentageNeededForDispute ? [
+ handleChangeNetworkParameter("percentageNeededForDispute", percentageForDispute, networkAddress)
+ ] : []),
+ ...(oracleExchangeRate !== forcedNetwork.oracleExchangeRate ? [
+ handleChangeNetworkParameter("oracleExchangeRate", oracleExchangeRate, networkAddress)
+ ] : []),
+ ...(mergeCreatorFeeShare !== forcedNetwork.mergeCreatorFeeShare ? [
+ handleChangeNetworkParameter("mergeCreatorFeeShare", mergeCreatorFeeShare, networkAddress)
+ ] : []),
+ ...(proposerFeeShare !== forcedNetwork.proposerFeeShare ? [
+ handleChangeNetworkParameter("proposerFeeShare", proposerFeeShare, networkAddress)
+ ] : []),
+ ...(cancelableTime !== +forcedNetwork.cancelableTime ? [
+ handleChangeNetworkParameter("cancelableTime", cancelableTime, networkAddress)
+ ] : [])
]);
const failed = [];
@@ -176,9 +173,8 @@ export default function MyNetworkSettings({
if (failed.length) {
dispatch(toastError(t("custom-network:errors.updated-parameters", {
- failed: failed.length,
- }),
- t("custom-network:errors.updating-values")));
+ failed: failed.length,
+ }), t("custom-network:errors.updating-values")));
console.error(failed);
}
@@ -230,7 +226,7 @@ export default function MyNetworkSettings({
state.Service?.active
.isRegistryGovernor(state.currentUser?.walletAddress)
.then(setIsGovernorRegistry);
- }, [state.currentUser?.walletAddress]);
+ }, [state.Service?.active, state.currentUser?.walletAddress]);
useEffect(() => {
if(!network) return;
@@ -288,19 +284,22 @@ export default function MyNetworkSettings({
isGovernorRegistry,
networkNeedRegistration,
errorBigImages
- ])
+ ]);
return (
<>
{isCurrentNetwork && (
)}
+
{!state.currentUser?.login && }
+
+
{settings?.validated &&
github?.validated &&
!network?.isClosed &&
diff --git a/components/profile/my-network-settings/registry-settings.tsx b/components/profile/my-network-settings/registry-settings.tsx
index c723740bf3..483b268941 100644
--- a/components/profile/my-network-settings/registry-settings.tsx
+++ b/components/profile/my-network-settings/registry-settings.tsx
@@ -13,29 +13,94 @@ import { WarningSpan } from "components/warning-span";
import { useAppState } from "contexts/app-state";
import { useNetworkSettings } from "contexts/network-settings";
+import { RegistryValidator, REGISTRY_LIMITS } from "helpers/registry";
+
+import { RegistryParameters } from "types/dappkit";
+
import useBepro from "x-hooks/use-bepro";
import { useNetwork } from "x-hooks/use-network";
+type Executing = "bountyFees" | "creationFee" | "creationAmount";
+
export default function RegistrySettings({ isGovernorRegistry = false }) {
const { t } = useTranslation(["common", "custom-network", "setup"]);
- const [networkCreationFeePercentage, setNetworkCreationFeePercentage] = useState()
- const [lockAmountForNetworkCreation, setLockAmountForNetworkCreation] = useState()
- const { fields, settings } = useNetworkSettings();
+
+ const [executingTx, setExecutingTx] = useState();
+ const [networkCreationFeePercentage, setNetworkCreationFeePercentage] = useState();
+ const [lockAmountForNetworkCreation, setLockAmountForNetworkCreation] = useState();
+
const {state} = useAppState();
const { updateActiveNetwork } = useNetwork();
+ const { fields, settings } = useNetworkSettings();
const { handleFeeSettings, handleAmountNetworkCreation, handleFeeNetworkCreation } = useBepro();
+ function validateLimits(param: RegistryParameters, value: string | number) {
+ if (RegistryValidator(param, value)) return undefined;
+
+ const { min, max } = REGISTRY_LIMITS[param] || {};
+
+ if (min !== undefined && max !== undefined)
+ return t("setup:registry.errors.exceeds-limit", { min, max });
+
+ if (min)
+ return t("setup:registry.errors.greater-than", { min });
+
+ if (max)
+ return t("setup:registry.errors.less-than", { max });
+
+ return t("setup:registry.errors.missing-limits");
+ }
+
+ const params = {
+ creationFee: {
+ isExecuting: executingTx === "creationFee",
+ error: validateLimits("networkCreationFeePercentage", networkCreationFeePercentage)
+ },
+ creationAmount: {
+ isExecuting: executingTx === "creationAmount",
+ error: validateLimits("lockAmountForNetworkCreation", lockAmountForNetworkCreation)
+ },
+ closeFee: {
+ isExecuting: executingTx === "bountyFees",
+ error: validateLimits("closeFeePercentage", settings?.treasury?.closeFee?.value)
+ },
+ cancelFee: {
+ isExecuting: executingTx === "bountyFees",
+ error: validateLimits("cancelFeePercentage", settings?.treasury?.cancelFee?.value)
+ }
+ };
+
+ const isExecuting = Object.entries(params).map(([, {isExecuting}]) => isExecuting).some(c => c);
+
+ const isSaveBountyFeesBtnDisabled = !!params.closeFee.error || !!params.cancelFee.error || isExecuting;
+ const isSaveCreationFeeBtnDisabled = !!params.creationFee.error || isExecuting;
+ const isSaveCreationAmountBtnDisabled = !!params.creationAmount.error || isExecuting;
+
async function saveFeeSettings() {
- await handleFeeSettings(settings?.treasury?.closeFee?.value,
- settings?.treasury?.cancelFee?.value).then(() => updateActiveNetwork(true));
+ setExecutingTx("bountyFees");
+
+ handleFeeSettings(settings?.treasury?.closeFee?.value, settings?.treasury?.cancelFee?.value)
+ .then(() => updateActiveNetwork(true))
+ .catch(console.debug)
+ .finally(() => setExecutingTx(undefined));
}
async function saveCreateNetworkFee() {
- await handleFeeNetworkCreation(Number(networkCreationFeePercentage)/100)
+ setExecutingTx("creationFee");
+
+ await handleFeeNetworkCreation(Number(networkCreationFeePercentage) / 100)
+ .then(() => updateActiveNetwork(true))
+ .catch(console.debug)
+ .finally(() => setExecutingTx(undefined));
}
async function saveCreateNetworkAmount() {
+ setExecutingTx("creationAmount");
+
await handleAmountNetworkCreation(lockAmountForNetworkCreation)
+ .then(() => updateActiveNetwork(true))
+ .catch(console.debug)
+ .finally(() => setExecutingTx(undefined));
}
useEffect(() => {
@@ -44,14 +109,7 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
state.Service.active.getRegistryCreatorAmount().then(v => setLockAmountForNetworkCreation(v.toFixed()))
state.Service.active.getRegistryCreatorFee().then(v => v.toString()).then(setNetworkCreationFeePercentage)
- },[state.Service.active])
-
- function exceedsFeesLimitsError(fee) {
- if (+fee < 0 || +fee > 100)
- return t("setup:registry.errors.exceeds-limit");
-
- return undefined;
- }
+ },[state.Service.active]);
return (
<>
@@ -84,6 +142,9 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
disabled={!isGovernorRegistry}
key="cancel-fee"
label={t("custom-network:steps.treasury.fields.cancel-fee.label")}
+ description={
+ t("custom-network:steps.treasury.fields.cancel-fee.description", REGISTRY_LIMITS["cancelFeePercentage"])
+ }
symbol="%"
value={settings?.treasury?.cancelFee?.value}
error={settings?.treasury?.cancelFee?.validated === false}
@@ -96,6 +157,9 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
disabled={!isGovernorRegistry}
key="close-fee"
label={t("custom-network:steps.treasury.fields.close-fee.label")}
+ description={
+ t("custom-network:steps.treasury.fields.close-fee.description", REGISTRY_LIMITS["closeFeePercentage"])
+ }
symbol="%"
value={settings?.treasury?.closeFee?.value}
error={settings?.treasury?.closeFee?.validated === false}
@@ -104,7 +168,13 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
{isGovernorRegistry && (
-
+
{t("custom-network:registry.save-fees-config")}
@@ -120,7 +190,7 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
onChange={setNetworkCreationFeePercentage}
variant="numberFormat"
description={t("setup:registry.fields.network-creation-fee.description")}
- error={exceedsFeesLimitsError(networkCreationFeePercentage)}
+ error={params.creationFee.error}
readOnly={!isGovernorRegistry}
/>
@@ -129,7 +199,9 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
{t("custom-network:registry.save-create-network-fee")}
@@ -145,11 +217,18 @@ export default function RegistrySettings({ isGovernorRegistry = false }) {
variant="numberFormat"
description={t("setup:registry.fields.network-creation-amount.description")}
readOnly={!isGovernorRegistry}
+ error={params.creationAmount.error}
/>
{isGovernorRegistry && (
-
- {t("custom-network:registry.save-create-network-amount")}
+
+ {t("custom-network:registry.save-create-network-amount")}
)}
diff --git a/components/proposal-action-card.tsx b/components/proposal-action-card.tsx
index b03e433d22..b5f71a7115 100644
--- a/components/proposal-action-card.tsx
+++ b/components/proposal-action-card.tsx
@@ -174,7 +174,7 @@ export default function ProposalActionCard({
className="flex-grow-1"
textClass="text-uppercase text-white"
color="purple"
- disabled={!proposalCanBeDisputed() || isDisputing}
+ disabled={isRefusing || isMerging || isDisputing || !proposalCanBeDisputed()}
onClick={handleDispute}
isLoading={isDisputing}
withLockIcon={!proposalCanBeDisputed() || isMerging || isRefusing}
diff --git a/components/proposal-progress-bar.tsx b/components/proposal-progress-bar.tsx
index d1596692db..47cdb61dc4 100644
--- a/components/proposal-progress-bar.tsx
+++ b/components/proposal-progress-bar.tsx
@@ -25,6 +25,8 @@ export default function ProposalProgressBar({
const [_columns, setColumns] = useState([]);
+ const totalNetworkToken = state.Service?.network?.amounts?.totalNetworkToken;
+
function toPercent(value = 0, total = 0, decimals = 2) {
return ((value / total) * 100).toFixed(decimals);
}
@@ -61,7 +63,7 @@ export default function ProposalProgressBar({
function loadDisputeState() {
setIssueState(getStateText());
setIssueColor(getStateColor());
- setPercentage(+toPercent(issueDisputeAmount, state.currentUser?.balance?.staked?.toNumber()));
+ setPercentage(+toPercent(issueDisputeAmount, +totalNetworkToken));
}
function renderColumn(dotLabel, index) {
@@ -127,7 +129,7 @@ export default function ProposalProgressBar({
{formatNumberToNScale(issueDisputeAmount)}{" "}
{" "}
- /{formatNumberToNScale(state.currentUser?.balance?.staked?.toNumber() || 0)}{" "}
+ /{formatNumberToNScale(totalNetworkToken || 0)}{" "}
{" "}
({percentage}%)
diff --git a/components/transaction-type.tsx b/components/transaction-type.tsx
index f2d5d4eda2..fb5679ee45 100644
--- a/components/transaction-type.tsx
+++ b/components/transaction-type.tsx
@@ -33,6 +33,10 @@ export default function TransactionType({ type }) {
[TransactionTypes.setDisputableTime]: t("transactions.types.set-disputable-time"),
[TransactionTypes.setPercentageNeededForDispute]: t("transactions.types.set-percentage-dispute"),
[TransactionTypes.setCouncilAmount]: t("transactions.types.set-council-amount"),
+ [TransactionTypes.setCancelableTime]: t("transactions.types.set-cancelable-time"),
+ [TransactionTypes.setOracleExchangeRate]: t("transactions.types.set-oracle-exchange-rate"),
+ [TransactionTypes.setProposerFeeShare]: t("transactions.types.set-proposer-fee-share"),
+ [TransactionTypes.setMergeCreatorFeeShare]: t("transactions.types.set-merge-creator-fee-share"),
[TransactionTypes.fundBounty]: t("transactions.types.fund-bounty"),
[TransactionTypes.retractFundBounty]: t("transactions.types.retract-fund"),
[TransactionTypes.withdrawFundRewardBounty]: t("transactions.types.withdraw-fund-reward-bounty"),
diff --git a/components/transactions-icon.tsx b/components/transactions-icon.tsx
index 8f9195a716..8cc8dc8798 100644
--- a/components/transactions-icon.tsx
+++ b/components/transactions-icon.tsx
@@ -49,6 +49,10 @@ export function TransactionIcon({ type } : TransactionIconProps) {
[TransactionTypes.setDisputableTime]:
,
[TransactionTypes.setPercentageNeededForDispute]:
,
[TransactionTypes.setCouncilAmount]:
,
+ [TransactionTypes.setCancelableTime]:
,
+ [TransactionTypes.setOracleExchangeRate]:
,
+ [TransactionTypes.setMergeCreatorFeeShare]:
,
+ [TransactionTypes.setProposerFeeShare]:
,
[TransactionTypes.fundBounty]:
,
[TransactionTypes.retractFundBounty]:
,
[TransactionTypes.withdrawFundRewardBounty]:
,
diff --git a/contexts/bounty-effects.tsx b/contexts/bounty-effects.tsx
index 4e41227be9..6fc5270e1c 100644
--- a/contexts/bounty-effects.tsx
+++ b/contexts/bounty-effects.tsx
@@ -23,6 +23,11 @@ export const BountyEffectsProvider = ({children}) => {
state.currentBounty?.data?.contractId,
state.currentUser?.walletAddress
])
+ useEffect(bounty.validateKycSteps, [
+ state?.currentBounty?.data?.isKyc,
+ state?.currentBounty?.data?.kycTierList,
+ state?.currentUser?.kycSession,
+ ]);
return
}
\ No newline at end of file
diff --git a/contexts/global-effects.tsx b/contexts/global-effects.tsx
index 58b7606c82..06f29a7fc7 100644
--- a/contexts/global-effects.tsx
+++ b/contexts/global-effects.tsx
@@ -43,6 +43,11 @@ export const GlobalEffectsProvider = ({children}) => {
// asPath.includes('bounty'),
// asPath.includes('profile'),
]);
+ useEffect(auth.updateKycSession, [state?.currentUser?.login,
+ state?.currentUser?.accessToken,
+ state?.currentUser?.match,
+ state?.currentUser?.walletAddress,
+ state?.Settings?.kyc?.tierList]);
useEffect(auth.updateWalletAddress, [state.currentUser]);
useEffect(auth.listenToAccountsChanged, [state.Service]);
useEffect(auth.updateWalletBalance, [state.currentUser?.walletAddress, state?.Service?.active?.network]);
diff --git a/contexts/network-settings.tsx b/contexts/network-settings.tsx
index e3f0c33cc0..89837c0d24 100644
--- a/contexts/network-settings.tsx
+++ b/contexts/network-settings.tsx
@@ -1,6 +1,5 @@
import {createContext, useContext, useEffect, useMemo, useState} from "react";
-import {Defaults} from "@taikai/dappkit";
import BigNumber from "bignumber.js";
import {useRouter} from "next/router";
@@ -9,14 +8,20 @@ import {useAppState} from "contexts/app-state";
import {isSameSet} from "helpers/array";
import {isColorsSimilar} from "helpers/colors";
import {
+ DEFAULT_CANCELABLE_TIME,
DEFAULT_CANCEL_FEE,
DEFAULT_CLOSE_FEE,
DEFAULT_COUNCIL_AMOUNT,
DEFAULT_DISPUTE_TIME,
DEFAULT_DRAFT_TIME,
- DEFAULT_PERCENTAGE_FOR_DISPUTE
-} from "helpers/contants";
+ DEFAULT_MERGER_FEE,
+ DEFAULT_ORACLE_EXCHANGE_RATE,
+ DEFAULT_PERCENTAGE_FOR_DISPUTE,
+ DEFAULT_PROPOSER_FEE
+} from "helpers/constants";
import {DefaultNetworkSettings} from "helpers/custom-network";
+import { NetworkValidator } from "helpers/network";
+import { RegistryValidator } from "helpers/registry";
import {Color, Network, NetworkSettings, Theme} from "interfaces/network";
import { Token } from "interfaces/token";
@@ -65,44 +70,47 @@ export const NetworkSettingsProvider = ({ children }) => {
useMemo(() =>
forcedNetwork || state.Service?.network?.active, [forcedNetwork, state.Service?.network?.active]);
- async function handlerValidateSettings(settings) {
+ function handlerValidateSettings(settings) {
//Treasury
- if (state.Service?.active) {
- const isAddressEmptyOrZeroAddress = settings?.treasury?.address?.value?.trim() === "" ||
- settings?.treasury?.address?.value === Defaults.nativeZeroAddress;
-
- const conditionOrUndefined = condition => isAddressEmptyOrZeroAddress ? undefined : condition;
-
- await Promise.all([
- conditionOrUndefined(state.Service?.active.isAddress(settings?.treasury?.address?.value)),
- conditionOrUndefined(settings?.treasury?.cancelFee?.value >= 0 && settings?.treasury?.cancelFee?.value <= 100),
- conditionOrUndefined(settings?.treasury?.closeFee?.value >= 0 && settings?.treasury?.closeFee?.value <= 100),
- ]).then((treasuryValidator)=>{
- settings.treasury.address.validated = treasuryValidator[0];
- settings.treasury.cancelFee.validated = treasuryValidator[1]
- settings.treasury.closeFee.validated = treasuryValidator[2]
- settings.treasury.validated = treasuryValidator.every(condition => condition !== false);
- })
-
- }
+ const isTreasuryEmpty = settings?.treasury?.address?.value?.trim() === "";
+ const ifEmptyThenUndefined = (condition: boolean) => isTreasuryEmpty ? undefined : condition;
+
+ const validations = [
+ ifEmptyThenUndefined(RegistryValidator("treasury", settings?.treasury?.address?.value)),
+ ifEmptyThenUndefined(RegistryValidator("cancelFeePercentage", settings?.treasury?.cancelFee?.value)),
+ ifEmptyThenUndefined(RegistryValidator("closeFeePercentage", settings?.treasury?.closeFee?.value))
+ ];
+
+ settings.treasury.address.validated = validations[0];
+ settings.treasury.cancelFee.validated = validations[1];
+ settings.treasury.closeFee.validated = validations[2];
+ settings.treasury.validated = validations.every(condition => condition !== false);
//Parameters
const parametersValidations = [
- Fields.parameter.validator("draftTime", settings?.parameters?.draftTime?.value),
- Fields.parameter.validator("councilAmount", settings?.parameters?.councilAmount?.value),
- Fields.parameter.validator("disputableTime", settings?.parameters?.disputableTime?.value),
- Fields.parameter.validator("percentageNeededForDispute",
- settings?.parameters?.percentageNeededForDispute?.value)
+ NetworkValidator("draftTime", settings?.parameters?.draftTime?.value),
+ NetworkValidator("councilAmount", settings?.parameters?.councilAmount?.value),
+ NetworkValidator("disputableTime", settings?.parameters?.disputableTime?.value),
+ NetworkValidator("percentageNeededForDispute", settings?.parameters?.percentageNeededForDispute?.value),
+ NetworkValidator("oracleExchangeRate", settings?.parameters?.oracleExchangeRate?.value),
+ NetworkValidator("mergeCreatorFeeShare", settings?.parameters?.mergeCreatorFeeShare?.value),
+ NetworkValidator("proposerFeeShare", settings?.parameters?.proposerFeeShare?.value),
+ NetworkValidator("cancelableTime", settings?.parameters?.cancelableTime?.value)
];
settings.parameters.draftTime.validated = parametersValidations[0];
settings.parameters.councilAmount.validated = parametersValidations[1];
settings.parameters.disputableTime.validated = parametersValidations[2];
settings.parameters.percentageNeededForDispute.validated = parametersValidations[3];
+ settings.parameters.oracleExchangeRate.validated = parametersValidations[4];
+ settings.parameters.mergeCreatorFeeShare.validated = parametersValidations[5];
+ settings.parameters.proposerFeeShare.validated = parametersValidations[6];
+ settings.parameters.cancelableTime.validated = parametersValidations[7];
settings.parameters.validated = parametersValidations.every(condition => condition);
+
//Theme
const colors = settings.theme?.colors;
-
+
if (colors?.primary){
const similar = [];
@@ -141,7 +149,7 @@ export const NetworkSettingsProvider = ({ children }) => {
Fields.repository.validator(newState.github?.repositories),
isCreating ? newState.github?.botPermission : true,
].every(condition => condition);
-
+
newState.settings = await handlerValidateSettings(newState.settings);
const settingsValidated = [
@@ -151,23 +159,28 @@ export const NetworkSettingsProvider = ({ children }) => {
newState.settings?.parameters?.disputableTime?.validated,
newState.settings?.parameters?.percentageNeededForDispute?.validated,
newState.settings?.parameters?.councilAmount?.validated,
+ newState.settings?.parameters?.oracleExchangeRate?.validated,
+ newState.settings?.parameters?.mergeCreatorFeeShare?.validated,
+ newState.settings?.parameters?.proposerFeeShare?.validated,
+ newState.settings?.parameters?.cancelableTime?.validated,
].every(condition => condition);
-
+
const tokensValidated = [
isCreating && newState.tokens?.settler?.trim() !== "" || true,
].every(condition => condition);
-
+
newState.tokensLocked.validated = tokensLockedValidate;
newState.details.validated = detailsValidate;
newState.github.validated = githubValidate;
newState.settings.validated = !!settingsValidated;
newState.tokens.validated = tokensValidated;
- newState.isSettingsValidated = [tokensLockedValidate,
- detailsValidate,
- githubValidate,
- settingsValidated,
- tokensValidated,
- ].every(condtion=> condtion)
+ newState.isSettingsValidated = [
+ tokensLockedValidate,
+ detailsValidate,
+ githubValidate,
+ settingsValidated,
+ tokensValidated
+ ].every(condtion=> condtion);
if(detailsValidate && isCreating){
const data = Object.keys(newState)
@@ -185,7 +198,7 @@ export const NetworkSettingsProvider = ({ children }) => {
const setFields = async (field: string, value: unknown) => {
const method = field.split('.')
-
+
if(!method) return;
const newState = { ...networkSettings };
@@ -209,7 +222,7 @@ export const NetworkSettingsProvider = ({ children }) => {
validator: async (value: string) => {
let validated = undefined;
- if (value.trim() !== "")
+ if (value.trim() !== "" && !isSetup)
validated = /bepro|taikai/gi.test(value) ? false : !(await getNetwork({name: value}).catch(() => false));
return !!validated;
@@ -221,9 +234,9 @@ export const NetworkSettingsProvider = ({ children }) => {
logo: {
setter: (value, type: "full" | "icon") => {
setFields(`details.${type}Logo`, {
- value,
- validated: value?.preview !== "" && value?.raw?.type?.includes("image/svg")
- })
+ value,
+ validated: value?.preview !== "" && value?.raw?.type?.includes("image/svg")
+ });
}
},
colors: {
@@ -261,9 +274,7 @@ export const NetworkSettingsProvider = ({ children }) => {
setter: value => setFields(`settings.treasury.closeFee.value`, value)
},
parameter: {
- setter: value => setFields(`settings.parameters.${[value.label]}.value`, value.value),
- validator:
- (parameter, value) => value >= (LIMITS[parameter]?.min || value) && value <= (LIMITS[parameter]?.max || value)
+ setter: value => setFields(`settings.parameters.${[value.label]}.value`, value.value)
}
};
@@ -367,28 +378,22 @@ export const NetworkSettingsProvider = ({ children }) => {
defaultState.settings.theme.colors = DefaultTheme();
+ const validatedParameter = value => ({ value, validated: true });
+
defaultState.settings.parameters = {
- draftTime: {
- value: DEFAULT_DRAFT_TIME,
- validated: undefined
- },
- disputableTime: {
- value: DEFAULT_DISPUTE_TIME,
- validated: undefined
- },
- percentageNeededForDispute: {
- value: DEFAULT_PERCENTAGE_FOR_DISPUTE,
- validated: undefined
- },
- councilAmount: {
- value: DEFAULT_COUNCIL_AMOUNT,
- validated: undefined
- },
- validated: undefined
- }
+ draftTime: validatedParameter(DEFAULT_DRAFT_TIME),
+ disputableTime: validatedParameter(DEFAULT_DISPUTE_TIME),
+ percentageNeededForDispute: validatedParameter(DEFAULT_PERCENTAGE_FOR_DISPUTE),
+ councilAmount: validatedParameter(DEFAULT_COUNCIL_AMOUNT),
+ cancelableTime: validatedParameter(DEFAULT_CANCELABLE_TIME),
+ oracleExchangeRate: validatedParameter(DEFAULT_ORACLE_EXCHANGE_RATE),
+ proposerFeeShare: validatedParameter(DEFAULT_PROPOSER_FEE),
+ mergeCreatorFeeShare: validatedParameter(DEFAULT_MERGER_FEE),
+ validated: true
+ };
- defaultState.settings.treasury.cancelFee = { value:DEFAULT_CANCEL_FEE, validated: true };
- defaultState.settings.treasury.closeFee = { value: DEFAULT_CLOSE_FEE, validated: true };
+ defaultState.settings.treasury.cancelFee = validatedParameter(DEFAULT_CANCEL_FEE);
+ defaultState.settings.treasury.closeFee = validatedParameter(DEFAULT_CLOSE_FEE);
defaultState.github.repositories = await loadGHRepos();
@@ -412,19 +417,27 @@ export const NetworkSettingsProvider = ({ children }) => {
}
setNetworkSettings(defaultState);
+
return defaultState;
}
async function loadNetworkSettings(): Promise
{
const defaultState = JSON.parse(JSON.stringify(DefaultNetworkSettings)); //Deep Copy, More: https://www.codingem.com/javascript-clone-object
- const service = await loadDaoService()
+ const service = await loadDaoService();
+
+ if (!service.network) return;
+
const [
treasury,
councilAmount,
disputableTime,
draftTime,
percentageNeededForDispute,
+ mergeCreatorFeeShare,
+ proposerFeeShare,
+ oracleExchangeRate,
+ cancelableTime,
isNetworkAbleToBeClosed,
] = await Promise.all([
service.network.treasuryInfo(),
@@ -432,57 +445,53 @@ export const NetworkSettingsProvider = ({ children }) => {
service.getNetworkParameter("disputableTime"),
service.getNetworkParameter("draftTime"),
service.getNetworkParameter("percentageNeededForDispute"),
+ service.getNetworkParameter("mergeCreatorFeeShare"),
+ service.getNetworkParameter("proposerFeeShare"),
+ service.getNetworkParameter("oracleExchangeRate"),
+ service.getNetworkParameter("cancelableTime"),
service.isNetworkAbleToBeClosed(),
- ])
+ ]);
+
+ const validatedParameter = value => ({ value, validated: true });
defaultState.settings.parameters = {
- draftTime: {
- value: +draftTime / 1000,
- validated: true
- },
- disputableTime: {
- value: +disputableTime / 1000,
- validated: true
- },
- percentageNeededForDispute: {
- value: +percentageNeededForDispute,
- validated: true
- },
- councilAmount: {
- value: +councilAmount,
- validated: true
- },
- validated: true
- }
+ draftTime: validatedParameter(+draftTime / 1000),
+ disputableTime: validatedParameter(+disputableTime / 1000),
+ percentageNeededForDispute: validatedParameter(percentageNeededForDispute),
+ councilAmount: validatedParameter(+councilAmount),
+ mergeCreatorFeeShare: validatedParameter(mergeCreatorFeeShare),
+ proposerFeeShare: validatedParameter(proposerFeeShare),
+ oracleExchangeRate: validatedParameter(oracleExchangeRate),
+ cancelableTime: validatedParameter(+cancelableTime / 1000),
+ validated: true
+ };
+
+ defaultState.settings.treasury.address = validatedParameter(treasury.treasury);
+ defaultState.settings.treasury.cancelFee = validatedParameter(treasury.cancelFee);
+ defaultState.settings.treasury.closeFee = validatedParameter(treasury.closeFee);
- defaultState.settings.treasury.address = {value: treasury.treasury, validated: true}
- defaultState.settings.treasury.cancelFee = { value: treasury.cancelFee, validated: true };
- defaultState.settings.treasury.closeFee = { value: treasury.closeFee, validated: true };
defaultState.tokens.allowedTransactions = network?.tokens?.filter(token => token.isTransactional);
defaultState.tokens.allowedRewards = network?.tokens?.filter(token => token.isReward);
- defaultState.details.name = {value: network?.name, validated: true}
+ defaultState.details.name = validatedParameter(network?.name);
defaultState.details.description = network?.description
- defaultState.details.fullLogo = {
- value: {
+ defaultState.details.fullLogo = validatedParameter({
preview:`${IPFS_URL}/${network?.fullLogo}`,
raw: undefined
- },
- validated: true
- }
- defaultState.details.iconLogo = {
- value: {
+ });
+
+ defaultState.details.iconLogo = validatedParameter({
preview:`${IPFS_URL}/${network?.logoIcon}`,
raw: undefined
- },
- validated: true
- }
+ });
+
defaultState.isAbleToClosed = isNetworkAbleToBeClosed;
defaultState.settings.theme.colors = network?.colors || DefaultTheme();
defaultState.github.repositories = await loadGHRepos();
- setNetworkSettings(defaultState)
+ setNetworkSettings(defaultState);
+
return defaultState;
}
@@ -527,7 +536,11 @@ export const NetworkSettingsProvider = ({ children }) => {
service.getNetworkParameter("councilAmount"),
service.getNetworkParameter("disputableTime"),
service.getNetworkParameter("draftTime"),
- service.getNetworkParameter("percentageNeededForDispute")
+ service.getNetworkParameter("percentageNeededForDispute"),
+ service.getNetworkParameter("cancelableTime"),
+ service.getNetworkParameter("oracleExchangeRate"),
+ service.getNetworkParameter("proposerFeeShare"),
+ service.getNetworkParameter("mergeCreatorFeeShare")
]))
.then(([
tokensLocked,
@@ -535,7 +548,12 @@ export const NetworkSettingsProvider = ({ children }) => {
councilAmount,
disputableTime,
draftTime,
- percentageNeededForDispute, ])=>
+ percentageNeededForDispute,
+ cancelableTime,
+ oracleExchangeRate,
+ proposerFeeShare,
+ mergeCreatorFeeShare
+ ])=>
setForcedNetwork((prev)=>({
...prev,
tokensLocked: tokensLocked.toFixed(),
@@ -544,8 +562,12 @@ export const NetworkSettingsProvider = ({ children }) => {
disputableTime: +disputableTime / 1000,
draftTime: +draftTime / 1000,
percentageNeededForDispute: +percentageNeededForDispute,
- })))
- },[forcedNetwork, state.Service?.active])
+ cancelableTime: +cancelableTime / 1000,
+ oracleExchangeRate: oracleExchangeRate,
+ proposerFeeShare: proposerFeeShare,
+ mergeCreatorFeeShare: mergeCreatorFeeShare
+ })));
+ },[forcedNetwork, state.Service?.active]);
useEffect(() => {
if (state.Service?.active?.registry?.contractAddress)
@@ -554,7 +576,6 @@ export const NetworkSettingsProvider = ({ children }) => {
.catch(error => console.debug("Failed to load registry token", error));
}, [state.Service?.active?.registry?.contractAddress]);
-
const memorizedValue = useMemo(() => ({
...networkSettings,
forcedNetwork,
diff --git a/contexts/reducers/change-current-bounty.ts b/contexts/reducers/change-current-bounty.ts
index 417d5628ea..4a936b6b3a 100644
--- a/contexts/reducers/change-current-bounty.ts
+++ b/contexts/reducers/change-current-bounty.ts
@@ -4,6 +4,8 @@ import {CurrentBounty, State} from "interfaces/application-state";
import {AppStateReduceId} from "interfaces/enums/app-state-reduce-id";
import { Token } from "interfaces/token";
+import {Tier} from 'types/settings'
+
import {BenefactorExtended, BountyExtended, ProposalExtended} from "../../interfaces/bounty";
import {IssueBigNumberData, IssueDataComment} from "../../interfaces/issue-data";
import {SimpleAction} from "./reducer";
@@ -21,6 +23,7 @@ export class ChangeCurrentBounty, A = k
switch (subAction) {
case 'comments':
case 'data':
+ case 'kycSteps':
case 'lastUpdated':
case 'chainData':
transformed = {...state.currentBounty, lastUpdated: +new Date(), ...payload}
@@ -91,4 +94,7 @@ export const changeCurrentBountyDataTransactional = (transactionalTokenData: Tok
changeCurrentBountyDataChain.update({transactionalTokenData});
export const changeCurrentBountyDataReward = (rewardTokenData: Token) =>
- changeCurrentBountyDataChain.update({rewardTokenData});
\ No newline at end of file
+ changeCurrentBountyDataChain.update({rewardTokenData});
+
+export const changeCurrentKycSteps = (kycSteps: Tier[]) =>
+ changeCurrentBounty.update({kycSteps}, 'kycSteps');
\ No newline at end of file
diff --git a/contexts/reducers/change-current-user.ts b/contexts/reducers/change-current-user.ts
index d7316fccaf..834c38c252 100644
--- a/contexts/reducers/change-current-user.ts
+++ b/contexts/reducers/change-current-user.ts
@@ -1,3 +1,5 @@
+import { kycSession } from "interfaces/kyc-session";
+
import {CurrentUserState, State} from "../../interfaces/application-state";
import {Balance} from "../../interfaces/balance-state";
import {AppStateReduceId} from "../../interfaces/enums/app-state-reduce-id";
@@ -43,4 +45,7 @@ export const changeCurrentUserConnected = (connected: boolean) =>
changeCurrentUser.update({connected})
export const changeCurrentUserSignature = (signature: string) =>
- changeCurrentUser.update({signature});
\ No newline at end of file
+ changeCurrentUser.update({signature});
+
+export const changeCurrentUserKycSession = (kycSession: kycSession) =>
+ changeCurrentUser.update({kycSession})
diff --git a/db/migrations/20221230112020-add-bounty-kyc-option.js b/db/migrations/20221230112020-add-bounty-kyc-option.js
new file mode 100644
index 0000000000..09a92ae676
--- /dev/null
+++ b/db/migrations/20221230112020-add-bounty-kyc-option.js
@@ -0,0 +1,21 @@
+'use strict';
+
+module.exports = {
+ async up (queryInterface, Sequelize) {
+ await queryInterface
+ .addColumn("issues", "isKyc", {
+ type: Sequelize.BOOLEAN,
+ default: false
+ })
+ await queryInterface
+ .addColumn("issues", "kycTierList", {
+ type: Sequelize.ARRAY(Sequelize.INTEGER),
+ default: []
+ })
+ },
+
+ async down (queryInterface, Sequelize) {
+ await queryInterface.removeColumn("issues", "isKyc");
+ await queryInterface.removeColumn("issues", "kycTierList");
+ }
+};
\ No newline at end of file
diff --git a/db/migrations/20221230115552-add-kyc-session.js b/db/migrations/20221230115552-add-kyc-session.js
new file mode 100644
index 0000000000..b40a0d89a3
--- /dev/null
+++ b/db/migrations/20221230115552-add-kyc-session.js
@@ -0,0 +1,54 @@
+"use strict";
+
+module.exports = {
+ async up(queryInterface, Sequelize) {
+ await queryInterface.createTable("kyc_sessions", {
+ id: {
+ type: Sequelize.INTEGER,
+ autoIncrement: true,
+ primaryKey: true,
+ unique: true,
+ },
+ user_id: {
+ type: Sequelize.INTEGER,
+ allowNull: false,
+ references: {
+ model: "users",
+ key: "id"
+ }
+ },
+ session_id: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ status: {
+ type: Sequelize.STRING,
+ },
+ steps:{
+ type: Sequelize.JSON,
+ },
+ tiers:{
+ type: Sequelize.ARRAY(Sequelize.INTEGER),
+ default: []
+ },
+ validatedAt: {
+ allowNull: true,
+ type: Sequelize.DATE,
+ },
+ createdAt: {
+ allowNull: false,
+ type: Sequelize.DATE,
+ defaultValue: Sequelize.fn('now')
+ },
+ updatedAt: {
+ allowNull: false,
+ type: Sequelize.DATE,
+ defaultValue: Sequelize.fn('now')
+ },
+ });
+ },
+
+ async down(queryInterface, Sequelize) {
+ await queryInterface.dropTable("kyc_sessions");
+ },
+};
diff --git a/db/models/index.js b/db/models/index.js
index 189d18dc20..e1a94f103c 100644
--- a/db/models/index.js
+++ b/db/models/index.js
@@ -19,6 +19,7 @@ import Disputes from './dispute-model';
import LeaderBoard from './leaderboard.model';
import ProposalDistributions from './proposal-distributions.model';
import HeaderInformation from './header-information';
+import KycSession from './kyc-session.model'
const Database = { sequelize: null };
@@ -45,6 +46,7 @@ Database.dispute = Disputes;
Database.leaderBoard = LeaderBoard;
Database.proposalDistributions = ProposalDistributions;
Database.headerInformation = HeaderInformation;
+Database.kycSession = KycSession;
Object.values(Database).forEach((model) => {
if (model?.init) {
diff --git a/db/models/issue.model.js b/db/models/issue.model.js
index 1c99cd8801..b0ef5d5680 100644
--- a/db/models/issue.model.js
+++ b/db/models/issue.model.js
@@ -38,6 +38,25 @@ class Issue extends Model {
tags: {
type: DataTypes.ARRAY(DataTypes.STRING)
},
+ isKyc:{
+ type: DataTypes.BOOLEAN,
+ default: false
+ },
+ kycTierList:{
+ type: DataTypes.ARRAY(DataTypes.INTEGER),
+ default: []
+ },
+ tags: {
+ type: DataTypes.ARRAY(DataTypes.STRING)
+ },
+ isKyc:{
+ type: DataTypes.BOOLEAN,
+ defaultValue: false
+ },
+ kycTierList:{
+ type: DataTypes.ARRAY(DataTypes.INTEGER),
+ default: []
+ }
},
{
sequelize,
diff --git a/db/models/kyc-session.model.js b/db/models/kyc-session.model.js
new file mode 100644
index 0000000000..bb7852d4de
--- /dev/null
+++ b/db/models/kyc-session.model.js
@@ -0,0 +1,35 @@
+"use strict";
+const { Model, DataTypes } = require("sequelize");
+
+class KycSession extends Model {
+ static init(sequelize) {
+ super.init({
+ user_id: DataTypes.INTEGER,
+ session_id: DataTypes.STRING,
+ status: {
+ type: DataTypes.STRING,
+ },
+ steps:{
+ type: DataTypes.JSON,
+ },
+ tiers:{
+ type: DataTypes.ARRAY(DataTypes.INTEGER),
+ default: []
+ },
+ validatedAt: {
+ allowNull: true,
+ type: DataTypes.DATE,
+ }
+ },
+ {
+ sequelize,
+ modelName: "kycSession",
+ tableName: "kyc_sessions"
+ });
+ }
+
+ static associate(models) {
+ }
+}
+
+module.exports = KycSession;
\ No newline at end of file
diff --git a/db/models/user.js b/db/models/user.js
index c3bdf02fa8..113fbf15b4 100644
--- a/db/models/user.js
+++ b/db/models/user.js
@@ -26,7 +26,10 @@ class User extends Model {
});
}
static associate(models) {
- // define association here
+ this.belongsTo(models.kycSession, {
+ foreignKey: "id",
+ sourceKey: "id"
+ });
}
}
diff --git a/helpers/contants.ts b/helpers/constants.ts
similarity index 74%
rename from helpers/contants.ts
rename to helpers/constants.ts
index 4ee6b1b9e8..2b10cc39e9 100644
--- a/helpers/contants.ts
+++ b/helpers/constants.ts
@@ -4,6 +4,10 @@ export const DEFAULT_DISPUTE_TIME = 259200;
export const DEFAULT_PERCENTAGE_FOR_DISPUTE = 3;
export const DEFAULT_DRAFT_TIME = 86400;
export const DEFAULT_COUNCIL_AMOUNT = 25000000;
+export const DEFAULT_ORACLE_EXCHANGE_RATE = 1;
+export const DEFAULT_MERGER_FEE = 0.05;
+export const DEFAULT_PROPOSER_FEE = 2;
+export const DEFAULT_CANCELABLE_TIME = 15811200;
export const BODY_CHARACTERES_LIMIT = 65536;
export const BOUNTY_TITLE_LIMIT = 131;
export const MAX_TAGS = 3;
@@ -14,4 +18,6 @@ export const NOT_ADMIN_WALLET = `Wrong wallet`;
export const MISSING_CHAIN_ID = `Missing chain id`;
export const IM_AM_CREATOR_ISSUE = `I am the owner of this bounty`;
export const NOT_AN_CREATOR_ISSUE = `Not an creator issue`;
-export const MISSING_CREATOR_ISSUE_SIGNATURE = `Missing creator issue signature`;
\ No newline at end of file
+export const MISSING_CREATOR_ISSUE_SIGNATURE = `Missing creator issue signature`;
+export const MAX_INTEGER_SOLIDITY = 2**256 - 1;
+export const NETWORK_DIVISOR = 1000000;
\ No newline at end of file
diff --git a/helpers/custom-network.ts b/helpers/custom-network.ts
index fbf8971de4..76c415cd72 100644
--- a/helpers/custom-network.ts
+++ b/helpers/custom-network.ts
@@ -3,6 +3,11 @@ import { Defaults } from "@taikai/dappkit";
import { Repository, ThemeColors } from "interfaces/network";
import { Token } from "interfaces/token";
+const ZeroField = () => ({
+ value: 0,
+ validated: undefined
+});
+
export const DefaultNetworkSettings = {
isSettingsValidated: false,
isAbleToClosed: false,
@@ -45,33 +50,19 @@ export const DefaultNetworkSettings = {
value: Defaults.nativeZeroAddress,
validated: undefined
},
- cancelFee: {
- value: 0,
- validated: undefined
- },
- closeFee: {
- value: 0,
- validated: undefined
- },
+ cancelFee: ZeroField(),
+ closeFee: ZeroField(),
validated: false
},
parameters: {
- draftTime: {
- value: 0,
- validated: undefined
- },
- disputableTime: {
- value: 0,
- validated: undefined
- },
- percentageNeededForDispute: {
- value: 0,
- validated: undefined
- },
- councilAmount: {
- value: 0,
- validated: undefined
- },
+ draftTime: ZeroField(),
+ disputableTime: ZeroField(),
+ percentageNeededForDispute: ZeroField(),
+ councilAmount: ZeroField(),
+ cancelableTime: ZeroField(),
+ oracleExchangeRate: ZeroField(),
+ proposerFeeShare: ZeroField(),
+ mergeCreatorFeeShare: ZeroField(),
validated: undefined
},
validated: undefined
diff --git a/helpers/handleNetworksValuesApi.ts b/helpers/handleNetworksValuesApi.ts
index afb7e99094..50e83787d6 100644
--- a/helpers/handleNetworksValuesApi.ts
+++ b/helpers/handleNetworksValuesApi.ts
@@ -5,7 +5,7 @@ const handleNetwork = (issues) =>
issue.network.dataValues = {
name: issue.network.name,
logoIcon: issue.network.logoIcon,
- colors: { primary: issue.network.colors.primary }
+ colors: { primary: issue.network.colors?.primary }
}
return issue
});
diff --git a/helpers/network.ts b/helpers/network.ts
new file mode 100644
index 0000000000..27608b2e4e
--- /dev/null
+++ b/helpers/network.ts
@@ -0,0 +1,48 @@
+import BigNumber from "bignumber.js";
+
+import { MAX_INTEGER_SOLIDITY, NETWORK_DIVISOR } from "helpers/constants";
+
+import { NetworkParameters } from "types/dappkit";
+
+type StrOrNmb = string | number;
+type Limit = {
+ min: StrOrNmb,
+ max: StrOrNmb
+};
+
+type NetworkLimits = {
+ [key in NetworkParameters]: Limit;
+}
+
+const betweenIn =
+ (value: StrOrNmb, min: StrOrNmb, max: StrOrNmb) => BigNumber(value).gte(min) && BigNumber(value).lte(max);
+
+const limits = (min?: StrOrNmb, max?: StrOrNmb) => ({ min, max });
+
+export const NETWORK_LIMITS: NetworkLimits = {
+ councilAmount: limits(1, 100000000000),
+ disputableTime: limits(60, 1728000),
+ draftTime: limits(60, 1728000),
+ oracleExchangeRate: limits(1),
+ mergeCreatorFeeShare: limits(0, 10),
+ proposerFeeShare: limits(0, 10),
+ percentageNeededForDispute: limits(0, 51),
+ cancelableTime: limits(15552000)
+};
+
+export const NetworkValidator = (param: NetworkParameters, value: StrOrNmb): boolean => {
+ const { min, max } = NETWORK_LIMITS[param] || {};
+
+ const validators = {
+ councilAmount: betweenIn(value, min, max),
+ disputableTime: betweenIn(value, min, max),
+ draftTime: betweenIn(value, min, max),
+ oracleExchangeRate: BigNumber(value).gte(min) && BigNumber(value).lte(MAX_INTEGER_SOLIDITY / NETWORK_DIVISOR),
+ mergeCreatorFeeShare: betweenIn(value, min, max),
+ proposerFeeShare: betweenIn(value, min, max),
+ percentageNeededForDispute: betweenIn(value, min, max),
+ cancelableTime: BigNumber(value).gte(min) && BigNumber(value).lte(MAX_INTEGER_SOLIDITY / NETWORK_DIVISOR)
+ };
+
+ return validators[param];
+};
\ No newline at end of file
diff --git a/helpers/query/findUserBySession.ts b/helpers/query/findUserBySession.ts
new file mode 100644
index 0000000000..862f0f2be6
--- /dev/null
+++ b/helpers/query/findUserBySession.ts
@@ -0,0 +1,27 @@
+import { NextApiRequest } from "next";
+import { getSession } from "next-auth/react";
+
+import models from "db/models";
+
+import {User} from "interfaces/api";
+
+async function findUserBySession(req: NextApiRequest): Promise {
+ const session = await getSession({ req }) as any;
+
+ if(!session?.user?.login)
+ return null
+
+ const user = await models.user.findOne({
+ where: {
+ githubLogin: session.user.login
+ },
+ raw: true
+ })
+
+ if(!user)
+ return null;
+
+ return user;
+}
+
+export default findUserBySession;
\ No newline at end of file
diff --git a/helpers/registry.ts b/helpers/registry.ts
new file mode 100644
index 0000000000..50e3ad40c7
--- /dev/null
+++ b/helpers/registry.ts
@@ -0,0 +1,33 @@
+import BigNumber from "bignumber.js";
+import { isZeroAddress } from "ethereumjs-util";
+import { isAddress } from "web3-utils";
+
+import { RegistryParameters } from "types/dappkit";
+
+type StrOrNmb = string | number;
+
+const betweenIn =
+ (value: StrOrNmb, min: StrOrNmb, max: StrOrNmb) => BigNumber(value).gte(min) && BigNumber(value).lte(max);
+
+const limits = (min?: StrOrNmb, max?: StrOrNmb) => ({ min, max });
+
+export const REGISTRY_LIMITS = {
+ closeFeePercentage: limits(0, 90),
+ cancelFeePercentage: limits(0, 100),
+ networkCreationFeePercentage: limits(0, 100),
+ lockAmountForNetworkCreation: limits(0)
+}
+
+export const RegistryValidator = (param: RegistryParameters, value: StrOrNmb) => {
+ const { min, max } = REGISTRY_LIMITS[param] || {};
+
+ const validators = {
+ closeFeePercentage: betweenIn(value, min, max),
+ cancelFeePercentage: betweenIn(value, min, max),
+ treasury: isAddress(value?.toString()) && !isZeroAddress(value?.toString()),
+ networkCreationFeePercentage: betweenIn(value, min, max),
+ lockAmountForNetworkCreation: BigNumber(value).gt(0)
+ }
+
+ return validators[param];
+}
\ No newline at end of file
diff --git a/interfaces/application-state.ts b/interfaces/application-state.ts
index 4222a3f0f9..c13e3b7810 100644
--- a/interfaces/application-state.ts
+++ b/interfaces/application-state.ts
@@ -4,11 +4,12 @@ import { TreasuryInfo } from "@taikai/dappkit";
import {XReducerAction} from "../contexts/reducers/reducer";
import DAO from "../services/dao-service";
-import {SettingsType} from "../types/settings";
+import {SettingsType, Tier} from "../types/settings";
import {Balance} from "./balance-state";
import {BountyExtended} from "./bounty";
import {BranchesList} from "./branches-list";
import {IssueBigNumberData, IssueDataComment} from "./issue-data";
+import { kycSession } from "./kyc-session";
import {LoadingState} from "./loading-state";
import {Network} from "./network";
import {ForkInfo, ForksList, RepoInfo, ReposList} from "./repos-list";
@@ -80,11 +81,14 @@ export interface CurrentUserState {
accessToken?: string;
connected?: boolean;
signature?: string;
+ kyc?: kycSession;
+ kycSession?: kycSession;
}
export interface CurrentBounty {
comments: IssueDataComment[];
lastUpdated: number;
+ kycSteps?: Tier[];
data: IssueBigNumberData;
chainData: BountyExtended;
}
diff --git a/interfaces/enums/transaction-types.ts b/interfaces/enums/transaction-types.ts
index 929a8622af..8b4b0f609a 100644
--- a/interfaces/enums/transaction-types.ts
+++ b/interfaces/enums/transaction-types.ts
@@ -25,6 +25,10 @@ export enum TransactionTypes {
setDisputableTime,
setPercentageNeededForDispute,
setCouncilAmount,
+ setCancelableTime,
+ setOracleExchangeRate,
+ setProposerFeeShare,
+ setMergeCreatorFeeShare,
fundBounty,
retractFundBounty,
withdrawFundRewardBounty,
diff --git a/interfaces/issue-data.ts b/interfaces/issue-data.ts
index 2c41be20e1..f8bfc6343d 100644
--- a/interfaces/issue-data.ts
+++ b/interfaces/issue-data.ts
@@ -1,9 +1,9 @@
import BigNumber from "bignumber.js";
-import { Proposal, INetworkProposal } from "interfaces/proposal";
-import { Token } from "interfaces/token";
+import {INetworkProposal, Proposal} from "interfaces/proposal";
+import {Token} from "interfaces/token";
-import { Payment } from "./payments";
+import {Payment} from "./payments";
export type IssueState =
| "pending"
@@ -63,6 +63,8 @@ export interface IssueData {
logoIcon: string;
}
tags: string[];
+ isKyc: boolean;
+ kycTierList: number[];
}
export interface Disputes {
@@ -80,8 +82,8 @@ export interface IssueBigNumberData extends Omit;
percentageNeededForDispute?: Field;
councilAmount?: Field;
+ cancelableTime?: Field;
+ oracleExchangeRate?: Field;
+ proposerFeeShare?: Field;
+ mergeCreatorFeeShare?: Field;
validated?: boolean;
};
validated: boolean;
diff --git a/middleware/admin-route.ts b/middleware/admin-route.ts
index 5e1602234f..c9827c4015 100644
--- a/middleware/admin-route.ts
+++ b/middleware/admin-route.ts
@@ -7,7 +7,7 @@ import {
MISSING_CHAIN_ID,
NOT_ADMIN_WALLET,
NOT_AN_ADMIN
-} from "helpers/contants";
+} from "helpers/constants";
import decodeMessage from "helpers/decode-message";
export const AdminRoute = (handler: NextApiHandler, methods: string[] = [`POST`, `PATCH`, `PUT`, `DELETE`]) => {
diff --git a/middleware/issue-route.ts b/middleware/issue-route.ts
index f86953727c..45284e5105 100644
--- a/middleware/issue-route.ts
+++ b/middleware/issue-route.ts
@@ -8,7 +8,7 @@ import {
NOT_AN_CREATOR_ISSUE,
MISSING_CREATOR_ISSUE_SIGNATURE,
MISSING_CHAIN_ID
-} from "helpers/contants";
+} from "helpers/constants";
import decodeMessage from "helpers/decode-message";
export const IssueRoute = (handler: NextApiHandler, methods: string[] = [ `PUT` ]) => {
diff --git a/next.config.js b/next.config.js
index 8430a96065..9cd9818448 100644
--- a/next.config.js
+++ b/next.config.js
@@ -9,7 +9,8 @@ const publicRuntimeConfig = {
api: process.env.NEXT_PUBLIC_API_HOST || "http://localhost:3000",
home: process.env.NEXT_PUBLIC_HOME_URL || "http://localhost:3000",
events: process.env.NEXT_PUBLIC_EVENTS_API || "http://localhost:3334",
- ipfs: process.env.NEXT_PUBLIC_IPFS_BASE
+ ipfs: process.env.NEXT_PUBLIC_IPFS_BASE,
+ kyc: process.env.NEXT_PUBLIC_KYC_API || 'https://individual-api.synaps.io/v3',
},
enableCoinGecko: process.env.NEXT_ENABLE_COINGECKO,
adminWallet: process.env.NEXT_PUBLIC_ADMIN_WALLET_ADDRESS,
@@ -22,7 +23,10 @@ const publicRuntimeConfig = {
proposalAccepted: process.env.NEXT_PUBLIC_PROPOSAL_ACCEPTED || 0.3,
proposalRejected: process.env.NEXT_PUBLIC_PROPOSAL_REJECTED || -0.5
},
- gaMeasureID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID
+ gaMeasureID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID,
+ kyc:{
+ isEnabled: process.env.NEXT_PUBLIC_ENABLE_KYC || false
+ },
}
// Will only be available on the server-side
@@ -39,6 +43,11 @@ const serverRuntimeConfig = {
owner: process.env.NEXT_GH_OWNER,
repository: process.env.NEXT_GH_REPO,
},
+ kyc:{
+ clientId: process.env.NEXT_SYNAPS_CLIENT_ID,
+ key: process.env.NEXT_SYNAPS_KEY,
+ defaultTier: process.env.NEXT_SYNAPS_TIER_ID
+ },
walletPrivateKey: process.env.NEXT_WALLET_PRIVATE_KEY,
elasticSearch: {
username: process.env.NEXT_ELASTIC_SEARCH_USERNAME,
diff --git a/package-lock.json b/package-lock.json
index de9e70f95b..f53a4331dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,8 @@
"@metamask/eth-sig-util": "^5.0.2",
"@metamask/jazzicon": "^2.0.0",
"@primer/css": "^20.8.0",
- "@taikai/dappkit": "^2.1.10",
+ "@synaps-io/react-verify": "^1.0.3",
+ "@taikai/dappkit": "^2.1.11",
"@taikai/scribal": "^1.0.3",
"@vlsergey/react-bootstrap-pagination": "^3.2.1",
"abort-controller": "^3.0.0",
@@ -2181,6 +2182,17 @@
"node": ">= 8"
}
},
+ "node_modules/@synaps-io/react-verify": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@synaps-io/react-verify/-/react-verify-1.0.3.tgz",
+ "integrity": "sha512-jsZLuHHjjnWz/p/+DpokHkLeMc7/i8N2AhHYA6FFOxT0xuJwMCq6vc8IHREvpAaTMiikvW7CqnAiPMRTI+Nvuw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17 || ^18"
+ }
+ },
"node_modules/@szmarczak/http-timer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -2193,9 +2205,9 @@
}
},
"node_modules/@taikai/dappkit": {
- "version": "2.1.10",
- "resolved": "https://registry.npmjs.org/@taikai/dappkit/-/dappkit-2.1.10.tgz",
- "integrity": "sha512-veh0iIJBLx7/SvnPouOjPW5+JgRiLxUvhjX1iDHUeI5qF7qANjM9izp3fJEDD2KM1jOrVvorzXIvXgh94QqygA==",
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/@taikai/dappkit/-/dappkit-2.1.11.tgz",
+ "integrity": "sha512-jyn98DFicbHkfunNoDlcvfJft3PxoE4ZPY0zTT/M8pCnfmMtgukJfwiBHlPLrcS1k7f7eXOszK3unku9LWA6Vg==",
"dependencies": {
"bignumber.js": "^9.0.2",
"date-fns": "^2.27.0",
@@ -15137,6 +15149,12 @@
"resolved": "https://registry.npmjs.org/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz",
"integrity": "sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw=="
},
+ "@synaps-io/react-verify": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@synaps-io/react-verify/-/react-verify-1.0.3.tgz",
+ "integrity": "sha512-jsZLuHHjjnWz/p/+DpokHkLeMc7/i8N2AhHYA6FFOxT0xuJwMCq6vc8IHREvpAaTMiikvW7CqnAiPMRTI+Nvuw==",
+ "requires": {}
+ },
"@szmarczak/http-timer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -15146,9 +15164,9 @@
}
},
"@taikai/dappkit": {
- "version": "2.1.10",
- "resolved": "https://registry.npmjs.org/@taikai/dappkit/-/dappkit-2.1.10.tgz",
- "integrity": "sha512-veh0iIJBLx7/SvnPouOjPW5+JgRiLxUvhjX1iDHUeI5qF7qANjM9izp3fJEDD2KM1jOrVvorzXIvXgh94QqygA==",
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/@taikai/dappkit/-/dappkit-2.1.11.tgz",
+ "integrity": "sha512-jyn98DFicbHkfunNoDlcvfJft3PxoE4ZPY0zTT/M8pCnfmMtgukJfwiBHlPLrcS1k7f7eXOszK3unku9LWA6Vg==",
"requires": {
"bignumber.js": "^9.0.2",
"date-fns": "^2.27.0",
diff --git a/package.json b/package.json
index 80e9485fba..88239354a5 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,8 @@
"@metamask/eth-sig-util": "^5.0.2",
"@metamask/jazzicon": "^2.0.0",
"@primer/css": "^20.8.0",
- "@taikai/dappkit": "^2.1.10",
+ "@synaps-io/react-verify": "^1.0.3",
+ "@taikai/dappkit": "^2.1.11",
"@taikai/scribal": "^1.0.3",
"@vlsergey/react-bootstrap-pagination": "^3.2.1",
"abort-controller": "^3.0.0",
diff --git a/pages/[network]/profile/index.tsx b/pages/[network]/profile/index.tsx
index d3fdb6cda6..8ab9f0c7b8 100644
--- a/pages/[network]/profile/index.tsx
+++ b/pages/[network]/profile/index.tsx
@@ -1,4 +1,4 @@
-import {useState} from "react";
+import React, {useState} from "react";
import {GetServerSideProps} from "next";
import {useTranslation} from "next-i18next";
@@ -17,6 +17,7 @@ import {truncateAddress} from "helpers/truncate-address";
import {useAuthentication} from "x-hooks/use-authentication";
import {useAppState} from "../../../contexts/app-state";
+import KycSessionModal from "../../../components/modals/kyc-session";
export default function Profile() {
const { t } = useTranslation("profile");
@@ -88,6 +89,10 @@ export default function Profile() {