From cef8466dbf51902c6b566a73c8b06f43702f7c39 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Wed, 30 Mar 2022 23:00:25 +0530 Subject: [PATCH 01/67] feat: Implement dynamic generic function editor form. --- .../ActionsModal/ContractActionsList.tsx | 76 ++++++---------- .../Guilds/ActionsModal/ContractsList.tsx | 91 +++++-------------- .../Guilds/ActionsModal/MintRepModal.tsx | 69 -------------- .../Guilds/ActionsModal/ParamsModal.tsx | 42 +++++++++ src/components/Guilds/ActionsModal/index.tsx | 9 +- src/components/Guilds/ActionsModal/styles.tsx | 58 ++++++++++++ 6 files changed, 159 insertions(+), 186 deletions(-) delete mode 100644 src/components/Guilds/ActionsModal/MintRepModal.tsx create mode 100644 src/components/Guilds/ActionsModal/ParamsModal.tsx create mode 100644 src/components/Guilds/ActionsModal/styles.tsx diff --git a/src/components/Guilds/ActionsModal/ContractActionsList.tsx b/src/components/Guilds/ActionsModal/ContractActionsList.tsx index b367a0b9aa..fcb6d0eb04 100644 --- a/src/components/Guilds/ActionsModal/ContractActionsList.tsx +++ b/src/components/Guilds/ActionsModal/ContractActionsList.tsx @@ -1,46 +1,12 @@ -import styled from 'styled-components'; -import { Flex } from '../common/Layout'; -import { ContainerText } from '../common/Layout/Text'; -import { Button } from '../common/Button'; import { RegistryContract } from 'hooks/Guilds/contracts/useContractRegistry'; - -const Wrapper = styled(Flex)` - margin: 16px auto; -`; -const WrapperText = styled(ContainerText).attrs(() => ({ - variant: 'bold', -}))` - justify-content: left; - flex-direction: row; - width: 85%; - margin: 8px auto; - color: ${({ theme }) => theme.colors.proposalText.grey}; -`; - -const ExternalWrapper = styled(Flex)` - width: 100%; - margin: 8px auto; -`; - -const ButtonText = styled(ContainerText).attrs(() => ({ - variant: 'medium', -}))` - justify-content: space-between; - flex-direction: row; -`; - -const ActionsButton = styled(Button)` - width: 90%; - margin: 6px 0; - flex-direction: column; - justify-content: left; - border-radius: 10px; - &:active, - &:focus { - border: 2px solid ${({ theme }) => theme.colors.text}; - } -`; - +import { + ActionsButton, + ButtonDetail, + ButtonLabel, + SectionTitle, + SectionWrapper, + Wrapper, +} from './styles'; interface ContractActionsListProps { contract: RegistryContract; onSelect: (functionName: string) => void; @@ -52,18 +18,32 @@ const ContractActionsList: React.FC = ({ }) => { return ( - - 6 Actions + + + {contract.functions.length}{' '} + {contract.functions.length >= 2 ? 'Actions' : 'Action'} + {contract.functions.map(contractFunction => ( onSelect(contractFunction.functionName)} > - {contractFunction.title} - {contractFunction.functionName} + {contractFunction.title} + + {contractFunction.functionName}( + {contractFunction.params.reduce( + (acc, cur, i) => + acc.concat( + cur.type, + i === contractFunction.params.length - 1 ? '' : ', ' + ), + '' + )} + ) + ))} - + ); }; diff --git a/src/components/Guilds/ActionsModal/ContractsList.tsx b/src/components/Guilds/ActionsModal/ContractsList.tsx index b9d922033d..061b30de27 100644 --- a/src/components/Guilds/ActionsModal/ContractsList.tsx +++ b/src/components/Guilds/ActionsModal/ContractsList.tsx @@ -1,8 +1,4 @@ import React from 'react'; -import styled from 'styled-components'; -import { Flex } from '../common/Layout'; -import { ContainerText } from '../common/Layout/Text'; -import { Button } from '../common/Button'; import { ReactComponent as Vector } from '../../../assets/images/vector.svg'; import StyledIcon from '../common/SVG'; import { @@ -11,57 +7,14 @@ import { } from 'hooks/Guilds/contracts/useContractRegistry'; import { useWeb3React } from '@web3-react/core'; import { SupportedAction } from '../ActionsBuilder/types'; - -const CoreWrapper = styled(Flex)` - width: 100%; - margin-bottom: 16px; -`; - -const ExternalWrapper = styled(Flex)` - width: 100%; -`; - -const Wrapper = styled(Flex)` - width: 100%; - margin: 24px auto; -`; - -const ActionsButton = styled(Button).attrs(() => ({ - variant: 'secondary', -}))` - width: 90%; - height: 40px; - margin: 6px 0; - flex-direction: row; - justify-content: left; - &:active, - &:focus { - border: 2px solid ${({ theme }) => theme.colors.text}; - } -`; - -const WrapperText = styled(ContainerText).attrs(() => ({ - variant: 'bold', -}))` - justify-content: left; - flex-direction: row; - width: 85%; - color: ${({ theme }) => theme.colors.proposalText.grey}; -`; - -const ButtonText = styled(ContainerText).attrs(() => ({ - variant: 'medium', -}))` - justify-content: space-between; - flex-direction: row; - color: ${({ theme }) => theme.colors.proposalText.grey}; -`; - -const ExternalButton = styled(ActionsButton).attrs(() => ({ - variant: 'secondary', -}))` - justify-content: space-between; -`; +import { + ActionsButton, + ButtonDetail, + ButtonLabel, + SectionTitle, + SectionWrapper, + Wrapper, +} from './styles'; interface ContractsListProps { onSelect: (contract: RegistryContract) => void; @@ -77,29 +30,31 @@ const ContractsList: React.FC = ({ return ( - - Core + + Core onSupportedActionSelect(SupportedAction.ERC20_TRANSFER) } > - - Transfer & Mint + + + Transfer & Mint + - - - External Contracts + + + External Contracts {contracts?.map(contract => ( - onSelect(contract)}> - {contract.title} - + onSelect(contract)}> + {contract.title} + {contract.functions?.length}{' '} {contract.functions?.length > 1 ? 'Actions' : 'Action'} - - + + ))} - + ); }; diff --git a/src/components/Guilds/ActionsModal/MintRepModal.tsx b/src/components/Guilds/ActionsModal/MintRepModal.tsx deleted file mode 100644 index 3e539124ca..0000000000 --- a/src/components/Guilds/ActionsModal/MintRepModal.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import styled from 'styled-components'; -import { Input } from '../../../components/Guilds/common/Form'; -import { useWeb3React } from '@web3-react/core'; -import { useState } from 'react'; -import { Flex } from '../common/Layout'; -import { ContainerText } from '../common/Layout/Text'; -import iconsByChain from '../common/ChainIcons'; - -const RepWrapper = styled(Flex)` - margin: 16px auto; -`; -const WrapperText = styled(ContainerText).attrs(() => ({ - variant: 'bold', -}))` - justify-content: left; - flex-direction: row; - width: 85%; - margin: 8px auto; - color: grey; -`; - -const ExternalWrapper = styled(Flex)` - width: 100%; - margin: 8px auto; -`; - -const MintReputationModal: React.FC = () => { - const [address, setAddress] = useState(''); - const [repAmount, setRepAmount] = useState(''); - const [repPercent, setRepPercent] = useState(''); - const { chainId } = useWeb3React(); - - return ( - - - Recipient - setAddress(e.target.value)} - icon={iconsByChain[chainId] || null} - size={24} - width="85%" - /> - - - Reputation Amount - setRepAmount(e.target.value)} - size={24} - width="85%" - /> - - - Reputation in % - setRepPercent(e.target.value)} - width="85%" - /> - - - ); -}; - -export default MintReputationModal; diff --git a/src/components/Guilds/ActionsModal/ParamsModal.tsx b/src/components/Guilds/ActionsModal/ParamsModal.tsx new file mode 100644 index 0000000000..44e100f9ec --- /dev/null +++ b/src/components/Guilds/ActionsModal/ParamsModal.tsx @@ -0,0 +1,42 @@ +import styled from 'styled-components'; +import { Input } from '../common/Form'; +import { useState } from 'react'; +import { ActionsButton, FormElement, FormLabel, Wrapper } from './styles'; +import { RegistryContractFunction } from 'hooks/Guilds/contracts/useContractRegistry'; + +const SubmitButton = styled(ActionsButton).attrs(() => ({ + variant: 'primary', +}))` + background-color: ${({ theme }) => theme.colors.button.primary}; + justify-content: center; +`; + +interface ParamsModalProps { + fn: RegistryContractFunction; +} + +const ParamsModal: React.FC = ({ fn }) => { + const [address, setAddress] = useState(''); + + return ( + + {fn.params.map(param => ( + + {param.description} + setAddress(e.target.value)} + size={24} + /> + + ))} + + + Add action + + + ); +}; + +export default ParamsModal; diff --git a/src/components/Guilds/ActionsModal/index.tsx b/src/components/Guilds/ActionsModal/index.tsx index d00f02a0d9..6d861fe2f3 100644 --- a/src/components/Guilds/ActionsModal/index.tsx +++ b/src/components/Guilds/ActionsModal/index.tsx @@ -6,6 +6,7 @@ import { DecodedAction, SupportedAction } from '../ActionsBuilder/types'; import { Modal } from '../common/Modal'; import ContractActionsList from './ContractActionsList'; import ContractsList from './ContractsList'; +import ParamsModal from './ParamsModal'; interface ActionModalProps { isOpen: boolean; @@ -40,7 +41,13 @@ const ActionModal: React.FC = ({ function getContent() { if (selectedFunction) { - return null; + return ( + fn.functionName === selectedFunction + )} + /> + ); } if (selectedContract) { diff --git a/src/components/Guilds/ActionsModal/styles.tsx b/src/components/Guilds/ActionsModal/styles.tsx new file mode 100644 index 0000000000..31d2f64203 --- /dev/null +++ b/src/components/Guilds/ActionsModal/styles.tsx @@ -0,0 +1,58 @@ +import styled from 'styled-components'; +import { Button } from '../common/Button'; +import { ContainerText } from '../common/Layout/Text'; + +export const Wrapper = styled.div` + width: 100%; +`; + +export const SectionWrapper = styled.div` + margin: 1.5rem; +`; + +export const ActionsButton = styled(Button).attrs(() => ({ + variant: 'secondary', +}))` + background-color: transparent; + padding: ${({ vertical }) => (vertical ? '1rem' : '0.75rem 1rem')}; + width: 100%; + display: flex; + align-items: ${({ vertical }) => (vertical ? 'flex-start' : 'center')}; + justify-content: space-between; + flex-direction: ${({ vertical }) => (vertical ? 'column' : 'row')}; + border-radius: ${({ vertical }) => (vertical ? '0.625rem' : '2rem')}; + &:active, + &:focus { + border: 2px solid ${({ theme }) => theme.colors.text}; + } +`; + +export const SectionTitle = styled(ContainerText).attrs(() => ({ + variant: 'bold', +}))` + display: block; + color: ${({ theme }) => theme.colors.proposalText.grey}; + margin-bottom: 0.75rem; + font-size: 0.875rem; +`; + +export const ButtonLabel = styled.div` + display: flex; + align-items: center; +`; + +export const ButtonDetail = styled(ContainerText).attrs(() => ({ + variant: 'medium', +}))` + margin: ${({ vertical }) => (vertical ? '0.5rem 0 0 0' : '0')}; + color: ${({ theme }) => theme.colors.proposalText.grey}; +`; + +export const FormElement = styled.div` + margin: 1.5rem; +`; + +export const FormLabel = styled.div` + color: ${({ theme }) => theme.colors.proposalText.grey}; + margin-bottom: 0.75rem; +`; \ No newline at end of file From 73e57aa1d89e50c0737fd4a83cb9375cc11fcf11 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Thu, 31 Mar 2022 14:27:54 +0530 Subject: [PATCH 02/67] chore: Fix code formatting. --- src/components/Guilds/ActionsModal/styles.tsx | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/components/Guilds/ActionsModal/styles.tsx b/src/components/Guilds/ActionsModal/styles.tsx index 31d2f64203..f781cd85c7 100644 --- a/src/components/Guilds/ActionsModal/styles.tsx +++ b/src/components/Guilds/ActionsModal/styles.tsx @@ -1,58 +1,58 @@ -import styled from 'styled-components'; -import { Button } from '../common/Button'; -import { ContainerText } from '../common/Layout/Text'; - -export const Wrapper = styled.div` - width: 100%; -`; - -export const SectionWrapper = styled.div` - margin: 1.5rem; -`; - -export const ActionsButton = styled(Button).attrs(() => ({ - variant: 'secondary', -}))` - background-color: transparent; - padding: ${({ vertical }) => (vertical ? '1rem' : '0.75rem 1rem')}; - width: 100%; - display: flex; - align-items: ${({ vertical }) => (vertical ? 'flex-start' : 'center')}; - justify-content: space-between; - flex-direction: ${({ vertical }) => (vertical ? 'column' : 'row')}; - border-radius: ${({ vertical }) => (vertical ? '0.625rem' : '2rem')}; - &:active, - &:focus { - border: 2px solid ${({ theme }) => theme.colors.text}; - } -`; - -export const SectionTitle = styled(ContainerText).attrs(() => ({ - variant: 'bold', -}))` - display: block; - color: ${({ theme }) => theme.colors.proposalText.grey}; - margin-bottom: 0.75rem; - font-size: 0.875rem; -`; - -export const ButtonLabel = styled.div` - display: flex; - align-items: center; -`; - -export const ButtonDetail = styled(ContainerText).attrs(() => ({ - variant: 'medium', -}))` - margin: ${({ vertical }) => (vertical ? '0.5rem 0 0 0' : '0')}; - color: ${({ theme }) => theme.colors.proposalText.grey}; -`; - -export const FormElement = styled.div` - margin: 1.5rem; -`; - -export const FormLabel = styled.div` - color: ${({ theme }) => theme.colors.proposalText.grey}; - margin-bottom: 0.75rem; -`; \ No newline at end of file +import styled from 'styled-components'; +import { Button } from '../common/Button'; +import { ContainerText } from '../common/Layout/Text'; + +export const Wrapper = styled.div` + width: 100%; +`; + +export const SectionWrapper = styled.div` + margin: 1.5rem; +`; + +export const ActionsButton = styled(Button).attrs(() => ({ + variant: 'secondary', +}))` + background-color: transparent; + padding: ${({ vertical }) => (vertical ? '1rem' : '0.75rem 1rem')}; + width: 100%; + display: flex; + align-items: ${({ vertical }) => (vertical ? 'flex-start' : 'center')}; + justify-content: space-between; + flex-direction: ${({ vertical }) => (vertical ? 'column' : 'row')}; + border-radius: ${({ vertical }) => (vertical ? '0.625rem' : '2rem')}; + &:active, + &:focus { + border: 2px solid ${({ theme }) => theme.colors.text}; + } +`; + +export const SectionTitle = styled(ContainerText).attrs(() => ({ + variant: 'bold', +}))` + display: block; + color: ${({ theme }) => theme.colors.proposalText.grey}; + margin-bottom: 0.75rem; + font-size: 0.875rem; +`; + +export const ButtonLabel = styled.div` + display: flex; + align-items: center; +`; + +export const ButtonDetail = styled(ContainerText).attrs(() => ({ + variant: 'medium', +}))` + margin: ${({ vertical }) => (vertical ? '0.5rem 0 0 0' : '0')}; + color: ${({ theme }) => theme.colors.proposalText.grey}; +`; + +export const FormElement = styled.div` + margin: 1.5rem; +`; + +export const FormLabel = styled.div` + color: ${({ theme }) => theme.colors.proposalText.grey}; + margin-bottom: 0.75rem; +`; From b4c970a204ee710621a6b9077401f4d48935299e Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Tue, 5 Apr 2022 11:52:59 +0530 Subject: [PATCH 03/67] feat: Update action builder option styles. --- .../Guilds/ActionsBuilder/Option/EditMode.tsx | 27 ++++++++++++++----- .../Guilds/ActionsBuilder/Option/ViewMode.tsx | 27 ++++++++++++++----- .../Guilds/ActionsBuilder/Option/styles.tsx | 9 ++++--- .../ActionsBuilder/common/DataTag/index.tsx | 17 ++++++++++++ .../common/EditButton/index.tsx | 13 +++++++++ .../ActionsBuilder/common/Grip/index.tsx | 13 +++++++++ .../Guilds/ActionsBuilder/index.tsx | 14 +++------- 7 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 src/components/Guilds/ActionsBuilder/common/DataTag/index.tsx create mode 100644 src/components/Guilds/ActionsBuilder/common/EditButton/index.tsx create mode 100644 src/components/Guilds/ActionsBuilder/common/Grip/index.tsx diff --git a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx index 8c460cb78d..6cf3e733dc 100644 --- a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx @@ -1,10 +1,13 @@ import { ProposalOptionTag } from '../common/ProposalOptionTag'; import AddButton from '../common/AddButton'; import ActionEditor from '../Action/EditMode'; -import { ActionCountLabel, DetailWrapper, OptionWrapper } from './styles'; +import { Detail, DetailWrapper, OptionWrapper } from './styles'; import { DecodedAction, Option } from '../types'; import { useState } from 'react'; import ActionModal from 'components/Guilds/ActionsModal'; +import Grip from '../common/Grip'; +import DataTag from '../common/DataTag'; +import EditButton from '../common/EditButton'; interface OptionRowProps { option: Option; @@ -31,11 +34,23 @@ const OptionEditMode: React.FC = ({ option, onChange }) => { return ( - - - {option?.actions?.length || 'No'} on-chain{' '} - {option?.actions?.length > 2 ? 'actions' : 'action'} - +
+ + + + + + + + + {option?.actions?.length || 'No'} on-chain{' '} + {option?.actions?.length >= 2 ? 'actions' : 'action'} + + +
+
+ Edit +
{option?.decodedActions?.map((action, index) => ( diff --git a/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx index 3e22ec2cbb..ab813fdac8 100644 --- a/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx @@ -1,7 +1,10 @@ import { ProposalOptionTag } from '../common/ProposalOptionTag'; import ActionView from '../Action/ViewMode'; -import { ActionCountLabel, DetailWrapper, OptionWrapper } from './styles'; +import { Detail, DetailWrapper, OptionWrapper } from './styles'; import { Option } from '../types'; +import DataTag from '../common/DataTag'; +import Grip from '../common/Grip'; +import EditButton from '../common/EditButton'; interface OptionRowProps { data: Option; @@ -11,11 +14,23 @@ const OptionViewMode: React.FC = ({ data }) => { return ( - - - {data?.actions?.length || 'No'} on-chain{' '} - {data?.actions?.length >= 2 ? 'actions' : 'action'} - +
+ + + + + + + + + {data?.actions?.length || 'No'} on-chain{' '} + {data?.actions?.length >= 2 ? 'actions' : 'action'} + + +
+
+ Edit +
{data?.actions?.map((action, index) => ( diff --git a/src/components/Guilds/ActionsBuilder/Option/styles.tsx b/src/components/Guilds/ActionsBuilder/Option/styles.tsx index 4ae71320b6..2ebd12178a 100644 --- a/src/components/Guilds/ActionsBuilder/Option/styles.tsx +++ b/src/components/Guilds/ActionsBuilder/Option/styles.tsx @@ -1,10 +1,6 @@ import styled from 'styled-components'; import { Box } from 'components/Guilds/common/Layout'; -export const ActionCountLabel = styled.span` - color: ${({ theme }) => theme.gray}; -`; - export const OptionWrapper = styled(Box)` padding: 1rem; `; @@ -15,3 +11,8 @@ export const DetailWrapper = styled(Box)` flex-direction: row; justify-content: space-between; `; + +export const Detail = styled(Box)` + display: inline-flex; + margin-right: 0.75rem; +`; diff --git a/src/components/Guilds/ActionsBuilder/common/DataTag/index.tsx b/src/components/Guilds/ActionsBuilder/common/DataTag/index.tsx new file mode 100644 index 0000000000..50eb4c7ff3 --- /dev/null +++ b/src/components/Guilds/ActionsBuilder/common/DataTag/index.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import styled from 'styled-components'; + +interface DataTagProps { + children: React.ReactElement; +} + +const DataTag = styled.span` + border-radius: 0.5rem; + padding: 0.25rem 0.5rem; + background: ${({ theme }) => theme.colors.muted}; + border: 1px solid ${({ theme }) => theme.colors.muted}; + color: ${({ color }) => color}; + border-color: ${({ color }) => color}; +`; + +export default DataTag; diff --git a/src/components/Guilds/ActionsBuilder/common/EditButton/index.tsx b/src/components/Guilds/ActionsBuilder/common/EditButton/index.tsx new file mode 100644 index 0000000000..6eb9d808a2 --- /dev/null +++ b/src/components/Guilds/ActionsBuilder/common/EditButton/index.tsx @@ -0,0 +1,13 @@ +import { Button } from 'components/Guilds/common/Button'; +import styled from 'styled-components'; + +const EditButton = styled(Button).attrs({ + variant: 'secondary', +})` + font-weight: ${({ theme }) => theme.fontWeights.medium}; + font-size: ${({ theme }) => theme.fontSizes.label}; + margin: 0; + padding: 0.25rem 0.75rem; +`; + +export default EditButton; diff --git a/src/components/Guilds/ActionsBuilder/common/Grip/index.tsx b/src/components/Guilds/ActionsBuilder/common/Grip/index.tsx new file mode 100644 index 0000000000..41924bab23 --- /dev/null +++ b/src/components/Guilds/ActionsBuilder/common/Grip/index.tsx @@ -0,0 +1,13 @@ +import styled from 'styled-components'; +import { FaGripVertical } from 'react-icons/fa'; + +const Grip = styled(FaGripVertical)` + cursor: grabbing; + color: ${({ theme }) => theme.colors.proposalText.lightGrey}; + + &:hover { + color: ${({ theme }) => theme.colors.text}; + } +`; + +export default Grip; diff --git a/src/components/Guilds/ActionsBuilder/index.tsx b/src/components/Guilds/ActionsBuilder/index.tsx index 24287e2801..01f2028d43 100644 --- a/src/components/Guilds/ActionsBuilder/index.tsx +++ b/src/components/Guilds/ActionsBuilder/index.tsx @@ -1,7 +1,5 @@ -import styled from 'styled-components'; import { useState } from 'react'; import { Header as CardHeader } from '../common/Card'; -import { Button as CommonButton } from '../common/Button'; import SidebarCard, { SidebarCardHeaderSpaced, } from 'components/Guilds/SidebarCard'; @@ -9,13 +7,7 @@ import EditMode from './EditMode'; import ViewMode from './ViewMode'; import { Option } from './types'; import { bulkEncodeCallsFromOptions } from 'hooks/Guilds/contracts/useEncodedCall'; - -const Button = styled(CommonButton)` - font-weight: ${({ theme }) => theme.fontWeights.medium}; - font-size: ${({ theme }) => theme.fontSizes.label}; - margin: 0; - padding: 0.25rem 0.75rem; -`; +import EditButton from './common/EditButton'; interface ActionsBuilderProps { options: Option[]; @@ -44,12 +36,12 @@ export const ActionsBuilder: React.FC = ({ Actions {editable && ( - + )} } From d609197ccf1dc76286c474ebf3258caacbbf9b6c Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Tue, 5 Apr 2022 12:06:33 +0530 Subject: [PATCH 04/67] feat: Update action builder action styles. --- .../Guilds/ActionsBuilder/Action/ViewMode.tsx | 41 ++++++++++++++----- .../Guilds/ActionsBuilder/Option/ViewMode.tsx | 13 ++++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx index 418c4332e4..38e4b5cc7e 100644 --- a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx @@ -8,6 +8,8 @@ import { useDecodedCall } from 'hooks/Guilds/contracts/useDecodedCall'; import { getInfoLineView, getSummaryView } from '../SupportedActions'; import CallDetails from '../CallDetails'; import { Call } from '../types'; +import Grip from '../common/Grip'; +import EditButton from '../common/EditButton'; const CardWrapperWithMargin = styled(CardWrapper)` margin-top: 0.8rem; @@ -29,10 +31,10 @@ const CardLabel = styled(Box)` const ChevronIcon = styled.span` cursor: pointer; - height: 1.25rem; - width: 1.25rem; + height: 1.4rem; + width: 1.4rem; border-radius: 50%; - border: 1px solid ${({ theme }) => theme.colors.proposalText.grey}; + border: 1px solid ${({ theme }) => theme.colors.muted}; display: inline-flex; justify-content: center; align-items: center; @@ -64,6 +66,20 @@ const TabButton = styled(Button)` `} `; +const GripWithMargin = styled(Grip)` + margin-right: 1rem; +`; + +const EditButtonWithMargin = styled(EditButton)` + margin-right: 0.625rem; +`; + +const CardActions = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + interface ActionViewProps { call: Call; } @@ -82,15 +98,20 @@ const ActionView: React.FC = ({ call }) => { + + {InfoLine && } - setExpanded(!expanded)}> - {expanded ? ( - - ) : ( - - )} - + + Edit + setExpanded(!expanded)}> + {expanded ? ( + + ) : ( + + )} + + {expanded && ( diff --git a/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx index ab813fdac8..08e3a0f55c 100644 --- a/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx @@ -1,3 +1,4 @@ +import styled from 'styled-components'; import { ProposalOptionTag } from '../common/ProposalOptionTag'; import ActionView from '../Action/ViewMode'; import { Detail, DetailWrapper, OptionWrapper } from './styles'; @@ -10,6 +11,10 @@ interface OptionRowProps { data: Option; } +const ActionsWrapper = styled.div` + margin-left: 1.75rem; +`; + const OptionViewMode: React.FC = ({ data }) => { return ( @@ -33,9 +38,11 @@ const OptionViewMode: React.FC = ({ data }) => { - {data?.actions?.map((action, index) => ( - - ))} + + {data?.actions?.map((action, index) => ( + + ))} + ); }; From 6c326c4e12bbed02f0418aee8f91c68df567d149 Mon Sep 17 00:00:00 2001 From: Milton Tulli Date: Tue, 5 Apr 2022 11:03:53 -0300 Subject: [PATCH 05/67] use conditional rendering for rep implementation feature (#764) * use conditional rendering for rep implementation features in sidebar & withdraw action * Fix modal styles, refactor, add dev script tokens config --- scripts/dev.ts | 32 +++- .../Guilds/Sidebar/MemberActions.tsx | 5 +- .../Guilds/StakeTokensModal/StakeTokens.tsx | 154 +++++++++++------- .../guild/useGuildImplementationType.ts | 42 ++++- 4 files changed, 159 insertions(+), 74 deletions(-) diff --git a/scripts/dev.ts b/scripts/dev.ts index 8a249efd04..964681f36a 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -55,15 +55,15 @@ async function main() { distribution: [ { address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', - amount: 1000, + amount: web3.utils.toWei('200'), }, { address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', - amount: 4000, + amount: web3.utils.toWei('50'), }, { address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', - amount: 10000, + amount: web3.utils.toWei('10'), }, ], }, @@ -73,15 +73,15 @@ async function main() { distribution: [ { address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', - amount: 1000, + amount: web3.utils.toWei('200'), }, { address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', - amount: 4000, + amount: web3.utils.toWei('40'), }, { address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', - amount: 10000, + amount: web3.utils.toWei('10'), }, ], }, @@ -581,6 +581,24 @@ async function main() { logoURI: 'https://s2.coinmarketcap.com/static/img/coins/200x200/5589.png', }, + { + address: addresses.RGT, + name: 'REP Guild Token on Localhost', + decimals: 18, + symbol: 'RGT', + fetchPrice: true, + logoURI: + 'https://s2.coinmarketcap.com/static/img/coins/200x200/5589.png', + }, + { + address: addresses.SGT, + name: 'Snapshot Guild Token on Localhost', + decimals: 18, + symbol: 'SGT', + fetchPrice: true, + logoURI: + 'https://s2.coinmarketcap.com/static/img/coins/200x200/5589.png', + }, ], guilds: [addresses.DXDGuild, addresses.REPGuild, addresses.SnapshotGuild], }; @@ -588,7 +606,7 @@ async function main() { mkdirSync(path.resolve(__dirname, '../src/configs/localhost'), { recursive: true, }); - await writeFileSync( + writeFileSync( path.resolve(__dirname, '../src/configs/localhost/config.json'), JSON.stringify(developConfig, null, 2) ); diff --git a/src/components/Guilds/Sidebar/MemberActions.tsx b/src/components/Guilds/Sidebar/MemberActions.tsx index 376d31b688..493330b1d6 100644 --- a/src/components/Guilds/Sidebar/MemberActions.tsx +++ b/src/components/Guilds/Sidebar/MemberActions.tsx @@ -26,6 +26,7 @@ import { useTransactions } from '../../../contexts/Guilds'; import { useERC20Guild } from '../../../hooks/Guilds/contracts/useContract'; import useVotingPowerPercent from '../../../hooks/Guilds/guild/useVotingPowerPercent'; import useBigNumberToNumber from '../../../hooks/Guilds/conversions/useBigNumberToNumber'; +import useGuildImplementationType from '../../../hooks/Guilds/guild/useGuildImplementationType'; import { Loading } from '../common/Loading'; const UserActionButton = styled(IconButton)` @@ -130,6 +131,8 @@ export const MemberActions = () => { const memberMenuRef = useRef(null); useDetectBlur(memberMenuRef, () => setShowMenu(false)); + + const { isRepGuild } = useGuildImplementationType(guildAddress); return ( <> @@ -195,7 +198,7 @@ export const MemberActions = () => { Increase Voting Power - {isUnlockable && ( + {isUnlockable && !isRepGuild && ( Withdraw )} diff --git a/src/components/Guilds/StakeTokensModal/StakeTokens.tsx b/src/components/Guilds/StakeTokensModal/StakeTokens.tsx index 130557920f..f3094cbef9 100644 --- a/src/components/Guilds/StakeTokensModal/StakeTokens.tsx +++ b/src/components/Guilds/StakeTokensModal/StakeTokens.tsx @@ -2,6 +2,7 @@ import { useMemo, useState } from 'react'; import styled, { css } from 'styled-components'; import { FiArrowRight, FiInfo } from 'react-icons/fi'; import moment from 'moment'; +import { useHistory, useLocation } from 'react-router'; import { useParams } from 'react-router-dom'; import { useWeb3React } from '@web3-react/core'; @@ -20,6 +21,7 @@ import NumericalInput from '../common/Form/NumericalInput'; import useVotingPowerPercent from '../../../hooks/Guilds/guild/useVotingPowerPercent'; import useStringToBigNumber from '../../../hooks/Guilds/conversions/useStringToBigNumber'; import useBigNumberToNumber from '../../../hooks/Guilds/conversions/useBigNumberToNumber'; +import useGuildImplementationType from '../../../hooks/Guilds/guild/useGuildImplementationType'; import { Loading } from '../common/Loading'; const GuestContainer = styled.div` @@ -51,7 +53,7 @@ const DaoTitle = styled(Heading)` const InfoItem = styled.div` display: flex; font-size: ${({ theme }) => theme.fontSizes.body}; - color: ${({ theme }) => theme.colors.muted}; + color: ${({ theme }) => theme.colors.card.grey}; margin-bottom: 0.4rem; `; @@ -84,7 +86,7 @@ const BaseFont = css` const InfoLabel = styled.span` ${BaseFont} - color: ${({ theme }) => theme.colors.muted}; + color: ${({ theme }) => theme.colors.card.grey}; `; const InfoValue = styled.span` @@ -111,7 +113,7 @@ const StakeAmountInput = styled(NumericalInput)` font-family: inherit; `; -const ButtonLock = styled(Button)` +const ActionButton = styled(Button)` width: 100%; margin-top: 22px; self-align: flex-end; @@ -188,7 +190,9 @@ export const StakeTokens = () => { stakeAmountParsed?.add(guildConfig?.totalLocked), 3 ); - + const history = useHistory(); + const location = useLocation(); + const { isRepGuild } = useGuildImplementationType(guildAddress); return ( @@ -199,19 +203,51 @@ export const StakeTokens = () => { )} - - {guildConfig?.lockTime ? ( - `${moment - .duration(guildConfig.lockTime.toNumber(), 'seconds') - .humanize()} staking period` - ) : ( - - )}{' '} - + {!isRepGuild && ( + + {guildConfig?.lockTime ? ( + `${moment + .duration(guildConfig.lockTime.toNumber(), 'seconds') + .humanize()} staking period` + ) : ( + + )}{' '} + + )} - + {!isRepGuild && ( + + + Balance: + + {tokenBalance && tokenInfo ? ( + roundedBalance + ) : ( + + )}{' '} + {tokenInfo?.symbol || ( + + )} + + + + + + + + )} + {isRepGuild && ( - Balance: + Balance {tokenBalance && tokenInfo ? ( roundedBalance @@ -223,17 +259,7 @@ export const StakeTokens = () => { )} - - - - - + )} Your voting power @@ -260,41 +286,51 @@ export const StakeTokens = () => { )} - - Unlock Date - - {isStakeAmountValid ? ( - <> - - {moment() - .add(guildConfig.lockTime.toNumber(), 'seconds') - .format('MMM Do, YYYY - h:mm a')} - {' '} - - - ) : ( - '-' - )} - - - {stakeAmountParsed && tokenAllowance?.gte(stakeAmountParsed) ? ( - - Lock{' '} - {tokenInfo?.symbol || ( - - )} - + {!isRepGuild && ( + + Unlock Date + + {isStakeAmountValid ? ( + <> + + {moment() + .add(guildConfig.lockTime.toNumber(), 'seconds') + .format('MMM Do, YYYY - h:mm a')} + {' '} + + + ) : ( + '-' + )} + + + )} + {!isRepGuild ? ( + stakeAmountParsed && tokenAllowance?.gte(stakeAmountParsed) ? ( + + Lock{' '} + {tokenInfo?.symbol || ( + + )} + + ) : ( + + Approve{' '} + {tokenInfo?.symbol || ( + + )}{' '} + Spending + + ) ) : ( - history.push(location.pathname + '/proposalType')} > - Approve{' '} - {tokenInfo?.symbol || ( - - )}{' '} - Spending - + Mint Rep + )} ); diff --git a/src/hooks/Guilds/guild/useGuildImplementationType.ts b/src/hooks/Guilds/guild/useGuildImplementationType.ts index badd8406e4..1ccd2e2d2e 100644 --- a/src/hooks/Guilds/guild/useGuildImplementationType.ts +++ b/src/hooks/Guilds/guild/useGuildImplementationType.ts @@ -4,14 +4,42 @@ import useJsonRpcProvider from '../web3/useJsonRpcProvider'; import { GuildImplementationType } from '../../../types/types.guilds.d'; import deployedHashedBytecodes from '../../../bytecodes/config.json'; +const defaultImplementation = deployedHashedBytecodes.find( + ({ type }) => type === GuildImplementationType.IERC20Guild +) ?? { + type: GuildImplementationType.IERC20Guild, + features: [], + bytecode_hash: '', +}; + +interface ImplementationTypeConfig { + type: string; + features: string[]; + bytecode_hash: string; +} + +interface ImplementationTypeConfigReturn extends ImplementationTypeConfig { + isRepGuild: boolean; + isSnapshotGuild: boolean; +} +const parseConfig = ( + config: ImplementationTypeConfig +): ImplementationTypeConfigReturn => { + return { + ...config, + isRepGuild: config.features.includes('REP'), + isSnapshotGuild: config.features.includes('SNAPSHOT'), + }; +}; + /** * @function useGuildImplementationType * @param {string} guildAddress * @returns {string} GuildImplementationType. 'SnapshotRepERC20Guild' | 'DXDGuild' | 'ERC20Guild' | 'IERC20Guild' */ -export default function useGuildImplementationType( +export default function useGuildImplementationTypeConfig( guildAddress: string -): GuildImplementationType { +): ImplementationTypeConfigReturn { const [guildBytecode, setGuildBytecode] = useState(''); const provider = useJsonRpcProvider(); @@ -23,15 +51,15 @@ export default function useGuildImplementationType( getBytecode(); }, [guildAddress, provider]); - const implementationType: GuildImplementationType = useMemo(() => { - if (!guildBytecode) return GuildImplementationType.IERC20Guild; + const implementationTypeConfig: ImplementationTypeConfig = useMemo(() => { + if (!guildBytecode) return defaultImplementation; const match = deployedHashedBytecodes.find( ({ bytecode_hash }) => guildBytecode === bytecode_hash ); - return match ? match.type : GuildImplementationType.IERC20Guild; // default to IERC20Guild - }, [guildBytecode]) as GuildImplementationType; + return match ? match : defaultImplementation; // default to IERC20Guild + }, [guildBytecode]); - return implementationType; + return parseConfig(implementationTypeConfig); } From 7a58355d35778f495659b4065057096b1be841a9 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Tue, 5 Apr 2022 20:00:14 +0530 Subject: [PATCH 06/67] feat: Update action builder view mode styles. --- .../Guilds/ActionsBuilder/Action/ViewMode.tsx | 7 ++- .../Guilds/ActionsBuilder/EditMode.tsx | 20 +++++-- .../Guilds/ActionsBuilder/Option/EditMode.tsx | 60 +++++++++++++------ .../Guilds/ActionsBuilder/index.tsx | 27 ++++----- 4 files changed, 71 insertions(+), 43 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx index 38e4b5cc7e..17f5070947 100644 --- a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx @@ -82,9 +82,10 @@ const CardActions = styled.div` interface ActionViewProps { call: Call; + isEditable?: boolean; } -const ActionView: React.FC = ({ call }) => { +const ActionView: React.FC = ({ call, isEditable }) => { const { decodedCall } = useDecodedCall(call); const [expanded, setExpanded] = useState(false); @@ -98,12 +99,12 @@ const ActionView: React.FC = ({ call }) => { - + {isEditable && } {InfoLine && } - Edit + {isEditable && Edit} setExpanded(!expanded)}> {expanded ? ( diff --git a/src/components/Guilds/ActionsBuilder/EditMode.tsx b/src/components/Guilds/ActionsBuilder/EditMode.tsx index 97f8981dcd..06718f99c6 100644 --- a/src/components/Guilds/ActionsBuilder/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/EditMode.tsx @@ -9,11 +9,16 @@ const AddOptionWrapper = styled(Box)` padding: 1rem; `; interface EditModeProps { + isEditable: boolean; options: Option[]; onChange: (options: Option[]) => void; } -const EditMode: React.FC = ({ options, onChange }) => { +const EditMode: React.FC = ({ + isEditable, + options, + onChange, +}) => { function addOption() { onChange([ ...options, @@ -37,15 +42,20 @@ const EditMode: React.FC = ({ options, onChange }) => { key={idx} option={option} onChange={updatedOption => updateOption(idx, updatedOption)} + isEditable={isEditable} /> {idx !== options.length - 1 && } ))} - - - - + {isEditable && ( + <> + + + + + + )} ); }; diff --git a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx index 6cf3e733dc..40abd7971d 100644 --- a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx @@ -1,3 +1,4 @@ +import styled from 'styled-components'; import { ProposalOptionTag } from '../common/ProposalOptionTag'; import AddButton from '../common/AddButton'; import ActionEditor from '../Action/EditMode'; @@ -8,13 +9,23 @@ import ActionModal from 'components/Guilds/ActionsModal'; import Grip from '../common/Grip'; import DataTag from '../common/DataTag'; import EditButton from '../common/EditButton'; +import ActionView from '../Action/ViewMode'; + +const ActionsWrapper = styled.div` + margin-left: ${({ indented }) => (indented ? '1.75rem' : '0')}; +`; interface OptionRowProps { option: Option; + isEditable?: boolean; onChange?: (updatedOption: Option) => void; } -const OptionEditMode: React.FC = ({ option, onChange }) => { +const OptionEditMode: React.FC = ({ + isEditable, + option, + onChange, +}) => { const [isActionsModalOpen, setIsActionsModalOpen] = useState(false); function addAction(action: DecodedAction) { @@ -35,9 +46,11 @@ const OptionEditMode: React.FC = ({ option, onChange }) => {
- - - + {isEditable && ( + + + + )} @@ -48,23 +61,34 @@ const OptionEditMode: React.FC = ({ option, onChange }) => {
-
- Edit -
+ {isEditable && ( +
+ Edit +
+ )}
- {option?.decodedActions?.map((action, index) => ( - updateAction(index, updatedAction)} - /> - ))} + + {option?.actions?.map((action, index) => ( + + ))} + - setIsActionsModalOpen(true)} - /> + {isEditable && + option?.decodedActions?.map((action, index) => ( + updateAction(index, updatedAction)} + /> + ))} + + {isEditable && ( + setIsActionsModalOpen(true)} + /> + )} = ({ options, onChange, }) => { - const [actionsEditMode, setActionsEditMode] = useState(editable); + const [isEditable, setIsEditable] = useState(editable); - const onEdit = () => setActionsEditMode(true); + const onEdit = () => setIsEditable(true); const onSave = () => { const encodedOptions = bulkEncodeCallsFromOptions(options); onChange(encodedOptions); - setActionsEditMode(false); + setIsEditable(false); }; return ( @@ -35,22 +34,16 @@ export const ActionsBuilder: React.FC = ({ header={ Actions - {editable && ( - (actionsEditMode ? onSave() : onEdit())} - > - {actionsEditMode ? 'Save' : 'Edit'} - - )} + (isEditable ? onSave() : onEdit())} + > + {isEditable ? 'Save' : 'Edit'} + } > - {actionsEditMode ? ( - - ) : ( - - )} + ); }; From 982cb4109c84ef69d126b496a19f1e7ce3db1406 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Wed, 6 Apr 2022 01:01:12 +0530 Subject: [PATCH 07/67] feat: Implement the new action buidler workflow. --- .../Guilds/ActionsBuilder/Action/EditMode.tsx | 33 ++++++++++------- .../Guilds/ActionsBuilder/Action/ViewMode.tsx | 22 +++++++---- .../Guilds/ActionsBuilder/Option/EditMode.tsx | 37 ++++++++++--------- .../ERC20Transfer/ERC20TransferEditor.tsx | 5 +-- .../ERC20Transfer/ERC20TransferInfoLine.tsx | 19 +++++----- .../ERC20Transfer/ERC20TransferSummary.tsx | 13 +++---- .../ERC20Transfer/Transfer/index.tsx | 24 ++++++------ .../ActionsBuilder/SupportedActions/index.tsx | 19 +++++----- src/components/Guilds/ActionsModal/index.tsx | 37 ++++++++++++++++--- .../Guilds/contracts/useContractInterface.ts | 9 +++++ src/hooks/Guilds/contracts/useDecodedCall.ts | 4 +- 11 files changed, 134 insertions(+), 88 deletions(-) create mode 100644 src/hooks/Guilds/contracts/useContractInterface.ts diff --git a/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx index 57abe5084f..979a58c7ad 100644 --- a/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx @@ -1,25 +1,30 @@ -import { getEditor } from '../SupportedActions'; -import { DecodedAction, DecodedCall } from '../types'; +// import { getEditor } from '../SupportedActions'; +// import { DecodedAction, DecodedCall } from '../types'; + +import { DecodedAction } from '../types'; +import ActionView from './ViewMode'; interface ActionEditorProps { action: DecodedAction; onChange: (updatedCall: DecodedAction) => void; } -const ActionEditor: React.FC = ({ action, onChange }) => { - const Editor = getEditor(action?.decodedCall?.callType); +const ActionEditor: React.FC = ({ action }) => { + // const Editor = getEditor(action?.decodedCall?.callType); + + // const updateCall = (updatedCall: DecodedCall) => { + // onChange({ ...action, decodedCall: updatedCall }); + // }; - const updateCall = (updatedCall: DecodedCall) => { - onChange({ ...action, decodedCall: updatedCall }); - }; + return ; - return ( - - ); + // return ( + // + // ); }; export default ActionEditor; diff --git a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx index 17f5070947..a228a87214 100644 --- a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx @@ -7,7 +7,7 @@ import { Button } from 'components/Guilds/common/Button'; import { useDecodedCall } from 'hooks/Guilds/contracts/useDecodedCall'; import { getInfoLineView, getSummaryView } from '../SupportedActions'; import CallDetails from '../CallDetails'; -import { Call } from '../types'; +import { Call, DecodedCall } from '../types'; import Grip from '../common/Grip'; import EditButton from '../common/EditButton'; @@ -81,12 +81,20 @@ const CardActions = styled.div` `; interface ActionViewProps { - call: Call; + call?: Call; + decodedCall?: DecodedCall; isEditable?: boolean; + onEdit?: () => void; } -const ActionView: React.FC = ({ call, isEditable }) => { - const { decodedCall } = useDecodedCall(call); +const ActionView: React.FC = ({ + call, + decodedCall: decodedCallFromProps, + isEditable, +}) => { + const { decodedCall: decodedCallFromCall } = useDecodedCall(call); + + const decodedCall = decodedCallFromCall || decodedCallFromProps; const [expanded, setExpanded] = useState(false); const [activeTab, setActiveTab] = useState(0); @@ -101,7 +109,7 @@ const ActionView: React.FC = ({ call, isEditable }) => { {isEditable && } - {InfoLine && } + {InfoLine && } {isEditable && Edit} @@ -137,13 +145,13 @@ const ActionView: React.FC = ({ call, isEditable }) => { {ActionSummary && activeTab === 0 && ( - + )} {(!ActionSummary || activeTab === 1) && ( - + )} diff --git a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx index 40abd7971d..5850e2f275 100644 --- a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx @@ -69,26 +69,27 @@ const OptionEditMode: React.FC = ({ - {option?.actions?.map((action, index) => ( - - ))} - + {!isEditable && + option?.actions?.map((action, index) => ( + + ))} - {isEditable && - option?.decodedActions?.map((action, index) => ( - updateAction(index, updatedAction)} - /> - ))} + {isEditable && + option?.decodedActions?.map((action, index) => ( + updateAction(index, updatedAction)} + /> + ))} - {isEditable && ( - setIsActionsModalOpen(true)} - /> - )} + {isEditable && ( + setIsActionsModalOpen(true)} + /> + )} + = ({ - call, - contract, + decodedCall, updateCall, }) => { return ( @@ -24,7 +23,7 @@ const ERC20TransferEditor: React.FC = ({ Transfers & Mint - + diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferInfoLine.tsx index 4f3e4476ca..2940510a05 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferInfoLine.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferInfoLine.tsx @@ -9,20 +9,17 @@ import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; import { Segment } from '../common/infoLine'; -const ERC20TransferInfoLine: React.FC = ({ - call, - decodedCall, -}) => { +const ERC20TransferInfoLine: React.FC = ({ decodedCall }) => { const parsedData = useMemo(() => { - if (!call || !decodedCall) return null; + if (!decodedCall) return null; return { - tokenAddress: call.to, + tokenAddress: decodedCall.to, amount: BigNumber.from(decodedCall.args._value), - source: call.from, + source: decodedCall.from, destination: decodedCall.args._to as string, }; - }, [call, decodedCall]); + }, [decodedCall]); const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); const roundedBalance = useBigNumberToNumber( @@ -53,7 +50,11 @@ const ERC20TransferInfoLine: React.FC = ({ size={24} /> - {ensName || shortenAddress(parsedData?.destination)} + + {ensName || parsedData?.destination + ? shortenAddress(parsedData?.destination) + : ''} + ); }; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferSummary.tsx index 19ee32aa69..ef30547a22 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferSummary.tsx @@ -9,20 +9,17 @@ import { ActionViewProps } from '..'; import { Segment } from '../common/infoLine'; import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; -const ERC20TransferSummary: React.FC = ({ - call, - decodedCall, -}) => { +const ERC20TransferSummary: React.FC = ({ decodedCall }) => { const parsedData = useMemo(() => { - if (!call || !decodedCall) return null; + if (!decodedCall) return null; return { - tokenAddress: call.to, + tokenAddress: decodedCall.to, amount: BigNumber.from(decodedCall.args._value), - source: call.from, + source: decodedCall.from, destination: decodedCall.args._to, }; - }, [call, decodedCall]); + }, [decodedCall]); const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); const roundedBalance = useBigNumberToNumber( diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx index ec581d0ea2..1d120c15a1 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx @@ -74,22 +74,22 @@ interface TransferState { destination: string; } -const Transfer: React.FC = ({ call, updateCall }) => { +const Transfer: React.FC = ({ decodedCall, updateCall }) => { const [isTokenPickerOpen, setIsTokenPickerOpen] = useState(false); const { chainId } = useWeb3React(); // parse transfer state from calls const parsedData = useMemo(() => { - if (!call) return null; + if (!decodedCall) return null; return { - source: call.from, - tokenAddress: call.to, - amount: call.args._value, - destination: call.args._to, + source: decodedCall.from, + tokenAddress: decodedCall.to, + amount: decodedCall.args._value, + destination: decodedCall.args._to, }; - }, [call]); + }, [decodedCall]); const validations = useMemo(() => { return { @@ -120,9 +120,9 @@ const Transfer: React.FC = ({ call, updateCall }) => { const setTransferAddress = (walletAddress: string) => { updateCall({ - ...call, + ...decodedCall, args: { - ...call.args, + ...decodedCall.args, _to: walletAddress, }, }); @@ -130,7 +130,7 @@ const Transfer: React.FC = ({ call, updateCall }) => { const setToken = (tokenAddress: string) => { updateCall({ - ...call, + ...decodedCall, to: tokenAddress, }); }; @@ -140,9 +140,9 @@ const Transfer: React.FC = ({ call, updateCall }) => { ? utils.parseUnits(value, tokenInfo?.decimals || 18) : null; updateCall({ - ...call, + ...decodedCall, args: { - ...call.args, + ...decodedCall.args, _value: amount, }, }); diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx index effcf97efa..373736d632 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx @@ -1,19 +1,20 @@ import { BigNumber, utils } from 'ethers'; import { DeepPartial, RequireAtLeastOne } from 'utils/types'; -import { Call, DecodedAction, DecodedCall, SupportedAction } from '../types'; +import { DecodedAction, DecodedCall, SupportedAction } from '../types'; import ERC20ABI from '../../../../abis/ERC20.json'; import ERC20TransferEditor from './ERC20Transfer/ERC20TransferEditor'; import ERC20TransferInfoLine from './ERC20Transfer/ERC20TransferInfoLine'; import ERC20TransferSummary from './ERC20Transfer/ERC20TransferSummary'; +export interface SupportedActionMetadata { + title: string; +} export interface ActionViewProps { - call: Call; decodedCall: DecodedCall; } export interface ActionEditorProps { - contract: utils.Interface; - call: DecodedCall; + decodedCall: DecodedCall; updateCall: (updatedCall: DecodedCall) => void; } @@ -23,35 +24,33 @@ type SupportedActionViews = { }; type SupportedActionEditors = RequireAtLeastOne<{ - bulkEditor?: React.FC; editor?: React.FC; }>; export const supportedActions: Record< SupportedAction, - SupportedActionViews & SupportedActionEditors + SupportedActionViews & SupportedActionEditors & SupportedActionMetadata > = { [SupportedAction.ERC20_TRANSFER]: { + title: 'Transfers & Mint', infoLineView: ERC20TransferInfoLine, summaryView: ERC20TransferSummary, editor: ERC20TransferEditor, }, [SupportedAction.GENERIC_CALL]: { + title: 'Generic Call', infoLineView: () =>
Generic Call
, editor: () =>
Generic Call Editor
, }, }; -let ERC20Contract = new utils.Interface(ERC20ABI); - export const defaultValues: Record< SupportedAction, DeepPartial > = { [SupportedAction.ERC20_TRANSFER]: { - contract: ERC20Contract, decodedCall: { - function: ERC20Contract.getFunction('transfer'), + function: new utils.Interface(ERC20ABI).getFunction('transfer'), to: '', value: BigNumber.from(0), args: { diff --git a/src/components/Guilds/ActionsModal/index.tsx b/src/components/Guilds/ActionsModal/index.tsx index d00f02a0d9..810a32608b 100644 --- a/src/components/Guilds/ActionsModal/index.tsx +++ b/src/components/Guilds/ActionsModal/index.tsx @@ -1,8 +1,16 @@ import { RegistryContract } from 'hooks/Guilds/contracts/useContractRegistry'; import React, { useState } from 'react'; import { useParams } from 'react-router-dom'; -import { defaultValues } from '../ActionsBuilder/SupportedActions'; -import { DecodedAction, SupportedAction } from '../ActionsBuilder/types'; +import { + defaultValues, + getEditor, + supportedActions, +} from '../ActionsBuilder/SupportedActions'; +import { + DecodedAction, + DecodedCall, + SupportedAction, +} from '../ActionsBuilder/types'; import { Modal } from '../common/Modal'; import ContractActionsList from './ContractActionsList'; import ContractsList from './ContractsList'; @@ -20,10 +28,13 @@ const ActionModal: React.FC = ({ }) => { const { guild_id: guildId } = useParams<{ guild_id?: string }>(); + const [selectedAction, setSelectedAction] = useState(null); const [selectedContract, setSelectedContract] = useState(null); const [selectedFunction, setSelectedFunction] = useState(null); + const [data, setData] = useState(null); + function getHeader() { if (selectedFunction) { return selectedContract.functions.find( @@ -35,6 +46,10 @@ const ActionModal: React.FC = ({ return selectedContract?.title; } + if (selectedAction) { + return supportedActions[selectedAction].title; + } + return 'Add action'; } @@ -52,10 +67,15 @@ const ActionModal: React.FC = ({ ); } + if (selectedAction) { + const Editor = getEditor(selectedAction); + return ; + } + return ( ); } @@ -65,16 +85,21 @@ const ActionModal: React.FC = ({ setSelectedFunction(null); } else if (selectedContract) { setSelectedContract(null); + } else if (selectedAction) { + setSelectedAction(null); } + + setData(null); } - function addSupportedAction(action: SupportedAction) { - const defaultDecodedAction = defaultValues[action]; + function setSupportedAction(action: SupportedAction) { + const defaultDecodedAction = defaultValues[action] as DecodedAction; if (!defaultDecodedAction) return null; defaultDecodedAction.decodedCall.from = guildId; defaultDecodedAction.decodedCall.callType = action; - onAddAction(defaultDecodedAction as DecodedAction); + setData(defaultDecodedAction.decodedCall); + setSelectedAction(action); } return ( diff --git a/src/hooks/Guilds/contracts/useContractInterface.ts b/src/hooks/Guilds/contracts/useContractInterface.ts new file mode 100644 index 0000000000..3353e5d9b6 --- /dev/null +++ b/src/hooks/Guilds/contracts/useContractInterface.ts @@ -0,0 +1,9 @@ +import { utils } from 'ethers'; + +const useContractInterface = (ABI: any) => { + let ERC20Contract = new utils.Interface(ABI); + + return ERC20Contract; +}; + +export default useContractInterface; diff --git a/src/hooks/Guilds/contracts/useDecodedCall.ts b/src/hooks/Guilds/contracts/useDecodedCall.ts index 92a90ca805..5cbf224b72 100644 --- a/src/hooks/Guilds/contracts/useDecodedCall.ts +++ b/src/hooks/Guilds/contracts/useDecodedCall.ts @@ -143,5 +143,7 @@ export const useDecodedCall = (call: Call) => { const { chainId } = useWeb3React(); const { contracts } = useContractRegistry(); - return decodeCall(call, contracts, chainId); + return call + ? decodeCall(call, contracts, chainId) + : { decodedCall: null, contract: null }; }; From 5860826045cf4a94fc88a55829de0b4240199457 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Wed, 6 Apr 2022 01:47:06 +0530 Subject: [PATCH 08/67] feat: Get ERC20 Transfer widget working again. --- .../Guilds/ActionsBuilder/Action/EditMode.tsx | 17 -- .../ERC20Transfer/ERC20TransferEditor.tsx | 243 ++++++++++++++--- .../ERC20Transfer/Transfer/index.tsx | 244 ------------------ .../SupportedActions/common/editor.tsx | 34 --- .../ActionsBuilder/SupportedActions/index.tsx | 5 +- src/components/Guilds/ActionsModal/index.tsx | 40 ++- 6 files changed, 256 insertions(+), 327 deletions(-) delete mode 100644 src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx delete mode 100644 src/components/Guilds/ActionsBuilder/SupportedActions/common/editor.tsx diff --git a/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx index 979a58c7ad..e2d2577f48 100644 --- a/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx @@ -1,6 +1,3 @@ -// import { getEditor } from '../SupportedActions'; -// import { DecodedAction, DecodedCall } from '../types'; - import { DecodedAction } from '../types'; import ActionView from './ViewMode'; @@ -10,21 +7,7 @@ interface ActionEditorProps { } const ActionEditor: React.FC = ({ action }) => { - // const Editor = getEditor(action?.decodedCall?.callType); - - // const updateCall = (updatedCall: DecodedCall) => { - // onChange({ ...action, decodedCall: updatedCall }); - // }; - return ; - - // return ( - // - // ); }; export default ActionEditor; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx index 788acd5d7b..81d0c30e44 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx @@ -1,35 +1,220 @@ -import { FiNavigation } from 'react-icons/fi'; -import { - EditorWrapper, - FooterWrapper, - HeaderWrapper, - IconWrapper, - TitleWrapper, -} from '../common/editor'; +import styled from 'styled-components'; +import { Input } from 'components/Guilds/common/Form/Input'; +import { FiChevronDown, FiX } from 'react-icons/fi'; +import Avatar from 'components/Guilds/Avatar'; +import { resolveUri } from 'utils/url'; +import { useWeb3React } from '@web3-react/core'; +import { useTokenList } from 'hooks/Guilds/tokens/useTokenList'; +import { useMemo, useState } from 'react'; import { ActionEditorProps } from '..'; -import Transfer from './Transfer'; -import AddButton from '../../common/AddButton'; +import { BigNumber, utils } from 'ethers'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; +import TokenPicker from 'components/Guilds/TokenPicker'; +import { Box } from 'components/Guilds/common/Layout'; +import { MAINNET_ID } from 'utils'; +import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; +import { baseInputStyles } from 'components/Guilds/common/Form/Input'; + +const Control = styled(Box)` + display: flex; + flex-direction: column; + margin: 0.75rem 0; + width: 100%; +`; + +const ControlLabel = styled(Box)` + margin-bottom: 0.75rem; +`; + +const ControlRow = styled(Box)` + display: flex; + align-items: stretch; + height: 100%; +`; + +const Spacer = styled(Box)` + margin-right: 1rem; +`; + +const ClickableIcon = styled(Box)` + display: flex; + align-items: center; + cursor: pointer; +`; + +const TransferAmountInput = styled(NumericalInput)` + ${baseInputStyles} + display: flex; + align-items: center; + width: 100%; + &:hover, + &:focus { + border: 0.1rem solid ${({ theme }) => theme.colors.text}; + } +`; + +interface TransferState { + source: string; + tokenAddress: string; + amount: BigNumber; + destination: string; +} + +const Transfer: React.FC = ({ decodedCall, updateCall }) => { + const [isTokenPickerOpen, setIsTokenPickerOpen] = useState(false); + + const { chainId } = useWeb3React(); + + // parse transfer state from calls + const parsedData = useMemo(() => { + if (!decodedCall) return null; + + return { + source: decodedCall.from, + tokenAddress: decodedCall.to, + amount: decodedCall.args._value, + destination: decodedCall.args._to, + }; + }, [decodedCall]); + + const validations = useMemo(() => { + return { + tokenAddress: utils.isAddress(parsedData?.tokenAddress), + amount: BigNumber.isBigNumber(parsedData?.amount), + destination: utils.isAddress(parsedData?.destination), + }; + }, [parsedData]); + + // Get token details from the token address + const { tokens } = useTokenList(chainId); + const token = useMemo(() => { + if (!parsedData?.tokenAddress || !tokens) return null; + + return tokens.find(({ address }) => address === parsedData.tokenAddress); + }, [tokens, parsedData]); + + const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); + const roundedBalance = useBigNumberToNumber( + parsedData?.amount, + tokenInfo?.decimals, + 10 + ); + const { imageUrl: destinationAvatarUrl } = useENSAvatar( + parsedData?.destination, + MAINNET_ID + ); + + const setTransferAddress = (walletAddress: string) => { + updateCall({ + ...decodedCall, + args: { + ...decodedCall.args, + _to: walletAddress, + }, + }); + }; + + const setToken = (tokenAddress: string) => { + updateCall({ + ...decodedCall, + to: tokenAddress, + }); + }; + + const setAmount = (value: string) => { + const amount = value + ? utils.parseUnits(value, tokenInfo?.decimals || 18) + : null; + updateCall({ + ...decodedCall, + args: { + ...decodedCall.args, + _value: amount, + }, + }); + }; -const ERC20TransferEditor: React.FC = ({ - decodedCall, - updateCall, -}) => { return ( - - - - - - Transfers & Mint - - - - - - - - +
+ + Recipient + + + {validations.destination && ( + + )} +
+ } + iconRight={ + parsedData?.destination ? ( + setTransferAddress('')}> + + + ) : null + } + placeholder="Ethereum address" + onChange={e => setTransferAddress(e.target.value)} + /> + + + + + + Amount + + + + + + + + + Asset + setIsTokenPickerOpen(true)}> + + {parsedData?.tokenAddress && ( + + )} + + } + iconRight={} + readOnly + /> + + + + + setIsTokenPickerOpen(false)} + onSelect={tokenAddress => { + setToken(tokenAddress); + setIsTokenPickerOpen(false); + }} + /> + ); }; -export default ERC20TransferEditor; +export default Transfer; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx deleted file mode 100644 index 1d120c15a1..0000000000 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/Transfer/index.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import styled from 'styled-components'; -import { Input } from 'components/Guilds/common/Form/Input'; -import { FiChevronDown, FiMoreHorizontal, FiX } from 'react-icons/fi'; -import { DetailWrapper } from '../../common/editor'; -import Avatar from 'components/Guilds/Avatar'; -import { resolveUri } from 'utils/url'; -import { useWeb3React } from '@web3-react/core'; -import { useTokenList } from 'hooks/Guilds/tokens/useTokenList'; -import { useMemo, useState } from 'react'; -import { ActionEditorProps } from '../..'; -import { BigNumber, utils } from 'ethers'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; -import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -import TokenPicker from 'components/Guilds/TokenPicker'; -import { Box } from 'components/Guilds/common/Layout'; -import { Button } from 'components/Guilds/common/Button'; -import { MAINNET_ID } from 'utils'; -import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; -import { baseInputStyles } from 'components/Guilds/common/Form/Input'; - -const Control = styled(Box)` - display: flex; - flex-direction: column; - margin: 0.75rem 0; - width: 100%; -`; - -const ControlLabel = styled(Box)` - margin-bottom: 0.75rem; -`; - -const ControlRow = styled(Box)` - display: flex; - align-items: stretch; - height: 100%; -`; - -const Spacer = styled(Box)` - margin-right: 1rem; -`; - -const MenuButton = styled(Button).attrs(() => ({ - variant: 'secondary', -}))` - border-radius: 50%; - height: 2.7rem; - width: 2.7rem; - padding: 0; - margin: 0; -`; - -const ClickableIcon = styled(Box)` - display: flex; - align-items: center; - cursor: pointer; -`; - -const TransferAmountInput = styled(NumericalInput)` - ${baseInputStyles} - display: flex; - align-items: center; - width: 100%; - &:hover, - &:focus { - border: 0.1rem solid ${({ theme }) => theme.colors.text}; - } -`; - -interface TransferState { - source: string; - tokenAddress: string; - amount: BigNumber; - destination: string; -} - -const Transfer: React.FC = ({ decodedCall, updateCall }) => { - const [isTokenPickerOpen, setIsTokenPickerOpen] = useState(false); - - const { chainId } = useWeb3React(); - - // parse transfer state from calls - const parsedData = useMemo(() => { - if (!decodedCall) return null; - - return { - source: decodedCall.from, - tokenAddress: decodedCall.to, - amount: decodedCall.args._value, - destination: decodedCall.args._to, - }; - }, [decodedCall]); - - const validations = useMemo(() => { - return { - tokenAddress: utils.isAddress(parsedData?.tokenAddress), - amount: BigNumber.isBigNumber(parsedData?.amount), - destination: utils.isAddress(parsedData?.destination), - }; - }, [parsedData]); - - // Get token details from the token address - const { tokens } = useTokenList(chainId); - const token = useMemo(() => { - if (!parsedData?.tokenAddress || !tokens) return null; - - return tokens.find(({ address }) => address === parsedData.tokenAddress); - }, [tokens, parsedData]); - - const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); - const roundedBalance = useBigNumberToNumber( - parsedData?.amount, - tokenInfo?.decimals, - 10 - ); - const { imageUrl: destinationAvatarUrl } = useENSAvatar( - parsedData?.destination, - MAINNET_ID - ); - - const setTransferAddress = (walletAddress: string) => { - updateCall({ - ...decodedCall, - args: { - ...decodedCall.args, - _to: walletAddress, - }, - }); - }; - - const setToken = (tokenAddress: string) => { - updateCall({ - ...decodedCall, - to: tokenAddress, - }); - }; - - const setAmount = (value: string) => { - const amount = value - ? utils.parseUnits(value, tokenInfo?.decimals || 18) - : null; - updateCall({ - ...decodedCall, - args: { - ...decodedCall.args, - _value: amount, - }, - }); - }; - - return ( - - - Recipient - - - {validations.destination && ( - - )} - - } - iconRight={ - parsedData?.destination ? ( - setTransferAddress('')}> - - - ) : null - } - placeholder="Ethereum address" - onChange={e => setTransferAddress(e.target.value)} - /> - -
- - - -
-
-
- - - - Asset - setIsTokenPickerOpen(true)}> - - {parsedData?.tokenAddress && ( - - )} - - } - iconRight={} - readOnly - /> - - - - - - - Amount - - - -
- - - -
-
-
-
- - setIsTokenPickerOpen(false)} - onSelect={tokenAddress => { - setToken(tokenAddress); - setIsTokenPickerOpen(false); - }} - /> -
- ); -}; - -export default Transfer; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/common/editor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/common/editor.tsx deleted file mode 100644 index 1d72dbcde4..0000000000 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/common/editor.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { CardWrapper, Header } from 'components/Guilds/common/Card'; -import { Box } from 'components/Guilds/common/Layout'; -import styled from 'styled-components'; - -export const EditorWrapper = styled(CardWrapper)` - margin: 0.8rem 0; -`; - -export const HeaderWrapper = styled(Header)` - display: flex; - align-items: center; - margin: 0.875rem; -`; - -export const IconWrapper = styled.span` - display: flex; - margin-right: 0.875rem; -`; - -export const TitleWrapper = styled(Box)` - font-size: ${({ theme }) => theme.fontSizes.body}; - font-weight: ${({ theme }) => theme.fontWeights.medium}; -`; - -export const DetailWrapper = styled(Box)` - color: ${({ theme }) => theme.colors.proposalText.grey}; - padding: 1.25rem; - border-top: 1px solid ${({ theme }) => theme.colors.border.initial}; -`; - -export const FooterWrapper = styled(DetailWrapper)` - color: ${({ theme }) => theme.colors.text}; - padding: 0.75rem 1.25rem; -`; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx index 373736d632..a8c0930f37 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx @@ -44,13 +44,16 @@ export const supportedActions: Record< }, }; +const ERC20Contract = new utils.Interface(ERC20ABI); + export const defaultValues: Record< SupportedAction, DeepPartial > = { [SupportedAction.ERC20_TRANSFER]: { + contract: ERC20Contract, decodedCall: { - function: new utils.Interface(ERC20ABI).getFunction('transfer'), + function: ERC20Contract.getFunction('transfer'), to: '', value: BigNumber.from(0), args: { diff --git a/src/components/Guilds/ActionsModal/index.tsx b/src/components/Guilds/ActionsModal/index.tsx index 810a32608b..056f58ccae 100644 --- a/src/components/Guilds/ActionsModal/index.tsx +++ b/src/components/Guilds/ActionsModal/index.tsx @@ -1,6 +1,8 @@ +import { utils } from 'ethers'; import { RegistryContract } from 'hooks/Guilds/contracts/useContractRegistry'; import React, { useState } from 'react'; import { useParams } from 'react-router-dom'; +import styled from 'styled-components'; import { defaultValues, getEditor, @@ -11,10 +13,20 @@ import { DecodedCall, SupportedAction, } from '../ActionsBuilder/types'; +import { Button } from '../common/Button'; import { Modal } from '../common/Modal'; import ContractActionsList from './ContractActionsList'; import ContractsList from './ContractsList'; +export const EditorWrapper = styled.div` + margin: 1.25rem; +`; + +export const BlockButton = styled(Button)` + margin-top: 1rem; + width: 100%; +`; + interface ActionModalProps { isOpen: boolean; setIsOpen: (isOpen: boolean) => void; @@ -28,7 +40,12 @@ const ActionModal: React.FC = ({ }) => { const { guild_id: guildId } = useParams<{ guild_id?: string }>(); + // Supported Actions const [selectedAction, setSelectedAction] = useState(null); + const [selectedActionContract, setSelectedActionContract] = + useState(null); + + // Generic calls const [selectedContract, setSelectedContract] = useState(null); const [selectedFunction, setSelectedFunction] = useState(null); @@ -69,7 +86,12 @@ const ActionModal: React.FC = ({ if (selectedAction) { const Editor = getEditor(selectedAction); - return ; + return ( + + + Save Action + + ); } return ( @@ -87,6 +109,7 @@ const ActionModal: React.FC = ({ setSelectedContract(null); } else if (selectedAction) { setSelectedAction(null); + setSelectedActionContract(null); } setData(null); @@ -100,6 +123,19 @@ const ActionModal: React.FC = ({ defaultDecodedAction.decodedCall.callType = action; setData(defaultDecodedAction.decodedCall); setSelectedAction(action); + setSelectedActionContract(defaultDecodedAction.contract); + } + + function saveSupportedAction() { + if (!selectedAction || !data || !setSelectedActionContract) return; + + const decodedAction: DecodedAction = { + decodedCall: data, + contract: selectedActionContract, + }; + + onAddAction(decodedAction); + setIsOpen(false); } return ( @@ -108,7 +144,7 @@ const ActionModal: React.FC = ({ onDismiss={() => setIsOpen(false)} header={getHeader()} maxWidth={300} - backnCross={!!selectedContract} + backnCross={!!selectedAction || !!selectedContract} prevContent={goBack} > {getContent()} From 4fd3dfc06a151b5544a56d5d5d6be4884bed4a70 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Wed, 6 Apr 2022 02:51:17 +0530 Subject: [PATCH 09/67] refactor: Simplify action builder code. --- .../{Option/EditMode.tsx => Option.tsx} | 38 ++++++++++---- .../Guilds/ActionsBuilder/Option/ViewMode.tsx | 50 ------------------- .../Guilds/ActionsBuilder/Option/styles.tsx | 18 ------- .../{EditMode.tsx => OptionsList.tsx} | 10 ++-- .../ERC20Transfer/ERC20TransferEditor.tsx | 2 +- .../Guilds/ActionsBuilder/ViewMode.tsx | 22 -------- .../Guilds/ActionsBuilder/index.tsx | 22 +++++--- 7 files changed, 47 insertions(+), 115 deletions(-) rename src/components/Guilds/ActionsBuilder/{Option/EditMode.tsx => Option.tsx} (72%) delete mode 100644 src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx delete mode 100644 src/components/Guilds/ActionsBuilder/Option/styles.tsx rename src/components/Guilds/ActionsBuilder/{EditMode.tsx => OptionsList.tsx} (84%) delete mode 100644 src/components/Guilds/ActionsBuilder/ViewMode.tsx diff --git a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Option.tsx similarity index 72% rename from src/components/Guilds/ActionsBuilder/Option/EditMode.tsx rename to src/components/Guilds/ActionsBuilder/Option.tsx index 5850e2f275..a856484037 100644 --- a/src/components/Guilds/ActionsBuilder/Option/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Option.tsx @@ -1,15 +1,31 @@ import styled from 'styled-components'; -import { ProposalOptionTag } from '../common/ProposalOptionTag'; -import AddButton from '../common/AddButton'; -import ActionEditor from '../Action/EditMode'; -import { Detail, DetailWrapper, OptionWrapper } from './styles'; -import { DecodedAction, Option } from '../types'; +import { ProposalOptionTag } from './common/ProposalOptionTag'; +import AddButton from './common/AddButton'; +import ActionEditor from './Action/EditMode'; +import { DecodedAction, Option } from './types'; import { useState } from 'react'; import ActionModal from 'components/Guilds/ActionsModal'; -import Grip from '../common/Grip'; -import DataTag from '../common/DataTag'; -import EditButton from '../common/EditButton'; -import ActionView from '../Action/ViewMode'; +import Grip from './common/Grip'; +import DataTag from './common/DataTag'; +import EditButton from './common/EditButton'; +import ActionView from './Action/ViewMode'; +import { Box } from 'components/Guilds/common/Layout'; + +export const OptionWrapper = styled(Box)` + padding: 1rem; +`; + +export const DetailWrapper = styled(Box)` + padding: 0.5rem 0; + display: flex; + flex-direction: row; + justify-content: space-between; +`; + +export const Detail = styled(Box)` + display: inline-flex; + margin-right: 0.75rem; +`; const ActionsWrapper = styled.div` margin-left: ${({ indented }) => (indented ? '1.75rem' : '0')}; @@ -21,7 +37,7 @@ interface OptionRowProps { onChange?: (updatedOption: Option) => void; } -const OptionEditMode: React.FC = ({ +const OptionRow: React.FC = ({ isEditable, option, onChange, @@ -103,4 +119,4 @@ const OptionEditMode: React.FC = ({ ); }; -export default OptionEditMode; +export default OptionRow; diff --git a/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx deleted file mode 100644 index 08e3a0f55c..0000000000 --- a/src/components/Guilds/ActionsBuilder/Option/ViewMode.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import styled from 'styled-components'; -import { ProposalOptionTag } from '../common/ProposalOptionTag'; -import ActionView from '../Action/ViewMode'; -import { Detail, DetailWrapper, OptionWrapper } from './styles'; -import { Option } from '../types'; -import DataTag from '../common/DataTag'; -import Grip from '../common/Grip'; -import EditButton from '../common/EditButton'; - -interface OptionRowProps { - data: Option; -} - -const ActionsWrapper = styled.div` - margin-left: 1.75rem; -`; - -const OptionViewMode: React.FC = ({ data }) => { - return ( - - -
- - - - - - - - - {data?.actions?.length || 'No'} on-chain{' '} - {data?.actions?.length >= 2 ? 'actions' : 'action'} - - -
-
- Edit -
-
- - - {data?.actions?.map((action, index) => ( - - ))} - -
- ); -}; - -export default OptionViewMode; diff --git a/src/components/Guilds/ActionsBuilder/Option/styles.tsx b/src/components/Guilds/ActionsBuilder/Option/styles.tsx deleted file mode 100644 index 2ebd12178a..0000000000 --- a/src/components/Guilds/ActionsBuilder/Option/styles.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import styled from 'styled-components'; -import { Box } from 'components/Guilds/common/Layout'; - -export const OptionWrapper = styled(Box)` - padding: 1rem; -`; - -export const DetailWrapper = styled(Box)` - padding: 0.5rem 0; - display: flex; - flex-direction: row; - justify-content: space-between; -`; - -export const Detail = styled(Box)` - display: inline-flex; - margin-right: 0.75rem; -`; diff --git a/src/components/Guilds/ActionsBuilder/EditMode.tsx b/src/components/Guilds/ActionsBuilder/OptionsList.tsx similarity index 84% rename from src/components/Guilds/ActionsBuilder/EditMode.tsx rename to src/components/Guilds/ActionsBuilder/OptionsList.tsx index 06718f99c6..ee7a8953b9 100644 --- a/src/components/Guilds/ActionsBuilder/EditMode.tsx +++ b/src/components/Guilds/ActionsBuilder/OptionsList.tsx @@ -1,20 +1,20 @@ import styled from 'styled-components'; import { Divider } from '../common/Divider'; import { Box } from '../common/Layout'; -import OptionEditMode from './Option/EditMode'; +import OptionRow from './Option'; import AddButton from './common/AddButton'; import { Option } from './types'; const AddOptionWrapper = styled(Box)` padding: 1rem; `; -interface EditModeProps { +interface OptionsListProps { isEditable: boolean; options: Option[]; onChange: (options: Option[]) => void; } -const EditMode: React.FC = ({ +const OptionsList: React.FC = ({ isEditable, options, onChange, @@ -38,7 +38,7 @@ const EditMode: React.FC = ({ <> {options?.map((option, idx) => ( <> - updateOption(idx, updatedOption)} @@ -60,4 +60,4 @@ const EditMode: React.FC = ({ ); }; -export default EditMode; +export default OptionsList; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx index 81d0c30e44..2e68875941 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx @@ -172,7 +172,7 @@ const Transfer: React.FC = ({ decodedCall, updateCall }) => { Amount diff --git a/src/components/Guilds/ActionsBuilder/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/ViewMode.tsx deleted file mode 100644 index 87d46d5c09..0000000000 --- a/src/components/Guilds/ActionsBuilder/ViewMode.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Option } from './types'; -import { Divider } from '../common/Divider'; -import OptionViewMode from './Option/ViewMode'; - -interface ViewModeProps { - options: Option[]; -} - -const ViewMode: React.FC = ({ options }) => { - return ( - <> - {options?.map((option, idx) => ( - <> - - {idx !== options.length - 1 && } - - ))} - - ); -}; - -export default ViewMode; diff --git a/src/components/Guilds/ActionsBuilder/index.tsx b/src/components/Guilds/ActionsBuilder/index.tsx index eb55d3228d..51bbc80295 100644 --- a/src/components/Guilds/ActionsBuilder/index.tsx +++ b/src/components/Guilds/ActionsBuilder/index.tsx @@ -3,7 +3,7 @@ import { Header as CardHeader } from '../common/Card'; import SidebarCard, { SidebarCardHeaderSpaced, } from 'components/Guilds/SidebarCard'; -import EditMode from './EditMode'; +import OptionsList from './OptionsList'; import { Option } from './types'; import { bulkEncodeCallsFromOptions } from 'hooks/Guilds/contracts/useEncodedCall'; import EditButton from './common/EditButton'; @@ -34,16 +34,22 @@ export const ActionsBuilder: React.FC = ({ header={ Actions - (isEditable ? onSave() : onEdit())} - > - {isEditable ? 'Save' : 'Edit'} - + {editable && ( + (isEditable ? onSave() : onEdit())} + > + {isEditable ? 'Save' : 'Edit'} + + )} } > - + ); }; From 8912cc5ceb04d9c26a09182f7c1bd3678388f05d Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Wed, 6 Apr 2022 17:28:35 +0530 Subject: [PATCH 10/67] feat: Reorder options by drag and drop. --- package.json | 4 ++ .../Guilds/ActionsBuilder/Option.tsx | 14 +++- .../Guilds/ActionsBuilder/OptionsList.tsx | 67 ++++++++++++++----- .../ProposalOptionTag/ProposalOptionTag.tsx | 5 +- .../ProposalOptionTag.test.tsx.snap | 3 +- src/components/Guilds/ActionsBuilder/types.ts | 3 +- src/hooks/Guilds/guild/useProposalCalls.ts | 8 ++- yarn.lock | 39 +++++++++++ 8 files changed, 118 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 80a0040b20..d01c4a2438 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,10 @@ }, "dependencies": { "@0xsequence/multicall": "^0.35.5", + "@dnd-kit/core": "^5.0.3", + "@dnd-kit/modifiers": "^5.0.0", + "@dnd-kit/sortable": "^6.0.1", + "@dnd-kit/utilities": "^3.1.0", "@nomiclabs/hardhat-truffle5": "^2.0.5", "@nomiclabs/hardhat-web3": "^2.0.0", "@testing-library/jest-dom": "^5.16.2", diff --git a/src/components/Guilds/ActionsBuilder/Option.tsx b/src/components/Guilds/ActionsBuilder/Option.tsx index a856484037..2e68c081e8 100644 --- a/src/components/Guilds/ActionsBuilder/Option.tsx +++ b/src/components/Guilds/ActionsBuilder/Option.tsx @@ -1,4 +1,6 @@ import styled from 'styled-components'; +import { CSS } from '@dnd-kit/utilities'; + import { ProposalOptionTag } from './common/ProposalOptionTag'; import AddButton from './common/AddButton'; import ActionEditor from './Action/EditMode'; @@ -10,6 +12,7 @@ import DataTag from './common/DataTag'; import EditButton from './common/EditButton'; import ActionView from './Action/ViewMode'; import { Box } from 'components/Guilds/common/Layout'; +import { useSortable } from '@dnd-kit/sortable'; export const OptionWrapper = styled(Box)` padding: 1rem; @@ -42,6 +45,8 @@ const OptionRow: React.FC = ({ option, onChange, }) => { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: option.id }); const [isActionsModalOpen, setIsActionsModalOpen] = useState(false); function addAction(action: DecodedAction) { @@ -58,12 +63,17 @@ const OptionRow: React.FC = ({ onChange({ ...option, decodedActions: updatedActions }); } + const dndStyles = { + transform: CSS.Translate.toString(transform), + transition, + }; + return ( - +
{isEditable && ( - + )} diff --git a/src/components/Guilds/ActionsBuilder/OptionsList.tsx b/src/components/Guilds/ActionsBuilder/OptionsList.tsx index ee7a8953b9..655c172c07 100644 --- a/src/components/Guilds/ActionsBuilder/OptionsList.tsx +++ b/src/components/Guilds/ActionsBuilder/OptionsList.tsx @@ -1,4 +1,18 @@ -import styled from 'styled-components'; +import styled, { useTheme } from 'styled-components'; +import { + DndContext, + DragEndEvent, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; import { Divider } from '../common/Divider'; import { Box } from '../common/Layout'; import OptionRow from './Option'; @@ -8,6 +22,7 @@ import { Option } from './types'; const AddOptionWrapper = styled(Box)` padding: 1rem; `; + interface OptionsListProps { isEditable: boolean; options: Option[]; @@ -19,12 +34,14 @@ const OptionsList: React.FC = ({ options, onChange, }) => { + const theme = useTheme(); function addOption() { onChange([ ...options, { - index: options.length, + id: `option-${options.length}`, label: `Option ${options.length + 1}`, + color: theme?.colors?.votes?.[options.length], decodedActions: [], }, ]); @@ -34,19 +51,39 @@ const OptionsList: React.FC = ({ onChange(options.map((o, i) => (i === index ? option : o))); } + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); + + function handleDragEnd(event: DragEndEvent) { + const { active, over } = event; + + if (active.id !== over.id) { + const items = [...options]; + const oldIndex = items.findIndex(item => item.id === active.id); + const newIndex = items.findIndex(item => item.id === over.id); + onChange(arrayMove(items, oldIndex, newIndex)); + } + } + return ( - <> - {options?.map((option, idx) => ( - <> - updateOption(idx, updatedOption)} - isEditable={isEditable} - /> - {idx !== options.length - 1 && } - - ))} + + + {options?.map((option, idx) => ( + <> + updateOption(idx, updatedOption)} + isEditable={isEditable} + /> + {idx !== options.length - 1 && } + + ))} + {isEditable && ( <> @@ -56,7 +93,7 @@ const OptionsList: React.FC = ({ )} - + ); }; diff --git a/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/ProposalOptionTag.tsx b/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/ProposalOptionTag.tsx index a40b734ad6..e7431aaa2b 100644 --- a/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/ProposalOptionTag.tsx +++ b/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/ProposalOptionTag.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import styled, { useTheme } from 'styled-components'; +import styled from 'styled-components'; import { Option } from '../../types'; interface ProposalActionTagProps { @@ -19,6 +19,5 @@ const Tag = styled.span` export const ProposalOptionTag: React.FC = ({ option, }) => { - const theme = useTheme(); - return {option.label}; + return {option.label}; }; diff --git a/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/__snapshots__/ProposalOptionTag.test.tsx.snap b/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/__snapshots__/ProposalOptionTag.test.tsx.snap index d6c594ddee..7ff986ff40 100644 --- a/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/__snapshots__/ProposalOptionTag.test.tsx.snap +++ b/src/components/Guilds/ActionsBuilder/common/ProposalOptionTag/__snapshots__/ProposalOptionTag.test.tsx.snap @@ -4,8 +4,7 @@ exports[`ProposalOptionTag Should match snapshot 1`] = `
For diff --git a/src/components/Guilds/ActionsBuilder/types.ts b/src/components/Guilds/ActionsBuilder/types.ts index 84a2202f89..4e91c72345 100644 --- a/src/components/Guilds/ActionsBuilder/types.ts +++ b/src/components/Guilds/ActionsBuilder/types.ts @@ -28,8 +28,9 @@ export interface DecodedAction { } export interface Option { - index: number; + id: string; label: string; + color: string; actions?: Call[]; decodedActions?: DecodedAction[]; } diff --git a/src/hooks/Guilds/guild/useProposalCalls.ts b/src/hooks/Guilds/guild/useProposalCalls.ts index 46b4441eed..768616f69c 100644 --- a/src/hooks/Guilds/guild/useProposalCalls.ts +++ b/src/hooks/Guilds/guild/useProposalCalls.ts @@ -1,3 +1,4 @@ +import { useTheme } from 'styled-components'; import { useWeb3React } from '@web3-react/core'; import { Call, Option } from 'components/Guilds/ActionsBuilder/types'; import { useMemo } from 'react'; @@ -12,6 +13,8 @@ const useProposalCalls = (guildId: string, proposalId: string) => { const { contracts } = useContractRegistry(); const { chainId } = useWeb3React(); + const theme = useTheme(); + const options: Option[] = useMemo(() => { if (!guildId || !proposalId || !proposal) return null; @@ -40,15 +43,16 @@ const useProposalCalls = (guildId: string, proposalId: string) => { } const encodedOptions: Option[] = splitCalls.map((calls, index) => ({ - index, + id: `option-${index}`, label: `Option ${index + 1}`, + color: theme?.colors?.votes?.[options.length], actions: calls.filter( call => call.data !== ZERO_HASH || !call.value?.isZero() ), })); return bulkDecodeCallsFromOptions(encodedOptions, contracts, chainId); - }, [proposal, proposalId, guildId, chainId, contracts]); + }, [theme, proposal, proposalId, guildId, chainId, contracts]); return { options, diff --git a/yarn.lock b/yarn.lock index 801e022b56..392669a7ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1489,6 +1489,45 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@dnd-kit/accessibility@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.0.tgz#b56e3750414fd907b7d6972b3116aa8f96d07fde" + integrity sha512-QwaQ1IJHQHMMuAGOOYHQSx7h7vMZPfO97aDts8t5N/MY7n2QTDSnW+kF7uRQ1tVBkr6vJ+BqHWG5dlgGvwVjow== + dependencies: + tslib "^2.0.0" + +"@dnd-kit/core@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-5.0.3.tgz#028d7e543e9fd3756f9cb857b34f818d73020810" + integrity sha512-IribcBLsaPqHdYLpc5xG0TqwYIaD+65offdEYxoKh38WDjzRxUjDmrt7hj9oa/ooUKL0epux20u+mBTd92i/zw== + dependencies: + "@dnd-kit/accessibility" "^3.0.0" + "@dnd-kit/utilities" "^3.1.0" + tslib "^2.0.0" + +"@dnd-kit/modifiers@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@dnd-kit/modifiers/-/modifiers-5.0.0.tgz#c2ba0bfdb6bed994affcfc4c86834795b7a7f029" + integrity sha512-ZVOfa5dKmvAPBxjjnI4QTm8fHV4/gMdS6k0zY5Z8p/p9HkL/3QEHn2fYgqM2zVDpxf5maqrZ4MvqXILSPNQPMQ== + dependencies: + "@dnd-kit/utilities" "^3.1.0" + tslib "^2.0.0" + +"@dnd-kit/sortable@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-6.0.1.tgz#08d046efa85c546fd21979836b007d6fea1001f9" + integrity sha512-FGpRHBcflpwWpSP8bMJ4zNcDywDXssZDJBwGYuHd1RqUU/+JX43pEU0u0OHw06LabEZMOLBbyWoIaZJ0abCOEA== + dependencies: + "@dnd-kit/utilities" "^3.1.0" + tslib "^2.0.0" + +"@dnd-kit/utilities@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.1.0.tgz#330ef24303da05f85ff20bb8f74b385c96caee58" + integrity sha512-2JBdIgjJ7xjMRpKagdMuYKWJdDoVVw9Wc6lfMrLjrq8t4QlMEjtsab5JVUlzWvHgANn7w3Y3VhalFfbp4dQrKg== + dependencies: + tslib "^2.0.0" + "@electron/get@^1.13.0": version "1.14.1" resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40" From 87f11ed272f307d72680b6926dc7fc020dd55fa9 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Wed, 6 Apr 2022 17:49:16 +0530 Subject: [PATCH 11/67] refactor: Cleanup Action component. --- .../{Action/ViewMode.tsx => Action.tsx} | 22 ++++++------- .../Guilds/ActionsBuilder/Action/EditMode.tsx | 13 -------- .../Guilds/ActionsBuilder/Option.tsx | 31 ++++++++++++------- 3 files changed, 30 insertions(+), 36 deletions(-) rename src/components/Guilds/ActionsBuilder/{Action/ViewMode.tsx => Action.tsx} (85%) delete mode 100644 src/components/Guilds/ActionsBuilder/Action/EditMode.tsx diff --git a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx b/src/components/Guilds/ActionsBuilder/Action.tsx similarity index 85% rename from src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx rename to src/components/Guilds/ActionsBuilder/Action.tsx index a228a87214..4718872ce0 100644 --- a/src/components/Guilds/ActionsBuilder/Action/ViewMode.tsx +++ b/src/components/Guilds/ActionsBuilder/Action.tsx @@ -5,11 +5,11 @@ import { FiChevronDown, FiChevronUp } from 'react-icons/fi'; import { useState } from 'react'; import { Button } from 'components/Guilds/common/Button'; import { useDecodedCall } from 'hooks/Guilds/contracts/useDecodedCall'; -import { getInfoLineView, getSummaryView } from '../SupportedActions'; -import CallDetails from '../CallDetails'; -import { Call, DecodedCall } from '../types'; -import Grip from '../common/Grip'; -import EditButton from '../common/EditButton'; +import { getInfoLineView, getSummaryView } from './SupportedActions'; +import CallDetails from './CallDetails'; +import { Call, DecodedAction } from './types'; +import Grip from './common/Grip'; +import EditButton from './common/EditButton'; const CardWrapperWithMargin = styled(CardWrapper)` margin-top: 0.8rem; @@ -82,19 +82,19 @@ const CardActions = styled.div` interface ActionViewProps { call?: Call; - decodedCall?: DecodedCall; + decodedAction?: DecodedAction; isEditable?: boolean; - onEdit?: () => void; + onEdit?: (updatedCall: DecodedAction) => void; } -const ActionView: React.FC = ({ +const ActionRow: React.FC = ({ call, - decodedCall: decodedCallFromProps, + decodedAction, isEditable, }) => { const { decodedCall: decodedCallFromCall } = useDecodedCall(call); - const decodedCall = decodedCallFromCall || decodedCallFromProps; + const decodedCall = decodedCallFromCall || decodedAction.decodedCall; const [expanded, setExpanded] = useState(false); const [activeTab, setActiveTab] = useState(0); @@ -160,4 +160,4 @@ const ActionView: React.FC = ({ ); }; -export default ActionView; +export default ActionRow; diff --git a/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx b/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx deleted file mode 100644 index e2d2577f48..0000000000 --- a/src/components/Guilds/ActionsBuilder/Action/EditMode.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { DecodedAction } from '../types'; -import ActionView from './ViewMode'; - -interface ActionEditorProps { - action: DecodedAction; - onChange: (updatedCall: DecodedAction) => void; -} - -const ActionEditor: React.FC = ({ action }) => { - return ; -}; - -export default ActionEditor; diff --git a/src/components/Guilds/ActionsBuilder/Option.tsx b/src/components/Guilds/ActionsBuilder/Option.tsx index 2e68c081e8..4228ccb8ce 100644 --- a/src/components/Guilds/ActionsBuilder/Option.tsx +++ b/src/components/Guilds/ActionsBuilder/Option.tsx @@ -3,16 +3,15 @@ import { CSS } from '@dnd-kit/utilities'; import { ProposalOptionTag } from './common/ProposalOptionTag'; import AddButton from './common/AddButton'; -import ActionEditor from './Action/EditMode'; import { DecodedAction, Option } from './types'; import { useState } from 'react'; import ActionModal from 'components/Guilds/ActionsModal'; import Grip from './common/Grip'; import DataTag from './common/DataTag'; import EditButton from './common/EditButton'; -import ActionView from './Action/ViewMode'; +import ActionRow from './Action'; import { Box } from 'components/Guilds/common/Layout'; -import { useSortable } from '@dnd-kit/sortable'; +import { SortableContext, useSortable } from '@dnd-kit/sortable'; export const OptionWrapper = styled(Box)` padding: 1rem; @@ -97,17 +96,25 @@ const OptionRow: React.FC = ({ {!isEditable && option?.actions?.map((action, index) => ( - + ))} - {isEditable && - option?.decodedActions?.map((action, index) => ( - updateAction(index, updatedAction)} - /> - ))} + {isEditable && ( + `${option.id}-${index}` + )} + > + {option?.decodedActions?.map((action, index) => ( + updateAction(index, updatedAction)} + /> + ))} + + )} {isEditable && ( Date: Thu, 7 Apr 2022 00:39:17 +0530 Subject: [PATCH 12/67] feat: Move actions between options by drag and drop. --- .../Guilds/ActionsBuilder/Action.tsx | 14 +- .../Guilds/ActionsBuilder/Option.tsx | 11 +- .../Guilds/ActionsBuilder/OptionsList.tsx | 290 +++++++++++++++++- src/components/Guilds/ActionsBuilder/types.ts | 1 + src/components/Guilds/ActionsModal/index.tsx | 1 + src/hooks/Guilds/contracts/useDecodedCall.ts | 1 + 6 files changed, 297 insertions(+), 21 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/Action.tsx b/src/components/Guilds/ActionsBuilder/Action.tsx index 4718872ce0..a7fdb684ef 100644 --- a/src/components/Guilds/ActionsBuilder/Action.tsx +++ b/src/components/Guilds/ActionsBuilder/Action.tsx @@ -1,4 +1,5 @@ import styled, { css } from 'styled-components'; +import { CSS } from '@dnd-kit/utilities'; import { Box } from 'components/Guilds/common/Layout'; import { CardWrapper, Header } from 'components/Guilds/common/Card'; import { FiChevronDown, FiChevronUp } from 'react-icons/fi'; @@ -10,6 +11,7 @@ import CallDetails from './CallDetails'; import { Call, DecodedAction } from './types'; import Grip from './common/Grip'; import EditButton from './common/EditButton'; +import { useSortable } from '@dnd-kit/sortable'; const CardWrapperWithMargin = styled(CardWrapper)` margin-top: 0.8rem; @@ -92,6 +94,9 @@ const ActionRow: React.FC = ({ decodedAction, isEditable, }) => { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: decodedAction?.id }); + const { decodedCall: decodedCallFromCall } = useDecodedCall(call); const decodedCall = decodedCallFromCall || decodedAction.decodedCall; @@ -103,11 +108,16 @@ const ActionRow: React.FC = ({ const InfoLine = getInfoLineView(decodedCall?.callType); const ActionSummary = getSummaryView(decodedCall?.callType); + const dndStyles = { + transform: CSS.Translate.toString(transform), + transition, + }; + return ( - + - {isEditable && } + {isEditable && } {InfoLine && } diff --git a/src/components/Guilds/ActionsBuilder/Option.tsx b/src/components/Guilds/ActionsBuilder/Option.tsx index 4228ccb8ce..efab9267b2 100644 --- a/src/components/Guilds/ActionsBuilder/Option.tsx +++ b/src/components/Guilds/ActionsBuilder/Option.tsx @@ -11,7 +11,11 @@ import DataTag from './common/DataTag'; import EditButton from './common/EditButton'; import ActionRow from './Action'; import { Box } from 'components/Guilds/common/Layout'; -import { SortableContext, useSortable } from '@dnd-kit/sortable'; +import { + SortableContext, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; export const OptionWrapper = styled(Box)` padding: 1rem; @@ -101,9 +105,8 @@ const OptionRow: React.FC = ({ {isEditable && ( `${option.id}-${index}` - )} + items={option.decodedActions.map(action => action.id)} + strategy={verticalListSortingStrategy} > {option?.decodedActions?.map((action, index) => ( void; } +const PLACEHOLDER_ID = 'placeholder'; + const OptionsList: React.FC = ({ isEditable, options, onChange, }) => { + const [activeId, setActiveId] = useState(null); + const lastOverId = useRef(null); + const [clonedOptions, setClonedOptions] = useState(null); + const recentlyMovedToNewContainer = useRef(false); + + useEffect(() => { + requestAnimationFrame(() => { + recentlyMovedToNewContainer.current = false; + }); + }, [options]); + const theme = useTheme(); function addOption() { onChange([ @@ -51,27 +73,265 @@ const OptionsList: React.FC = ({ onChange(options.map((o, i) => (i === index ? option : o))); } - const sensors = useSensors( - useSensor(PointerSensor), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }) - ); + const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor)); + + const findContainer = (id: string) => { + if (options.find(option => option.id === id)) { + return id; + } + + return options + .map(option => ({ + id: option.id, + keys: option.decodedActions.map(action => action.id), + })) + .find(keyMap => keyMap.keys.includes(id))?.id; + }; + + function handleDragStart(event: DragStartEvent) { + setActiveId(event.active.id); + setClonedOptions(options); + } + + function handleDragOver({ active, over }: DragOverEvent) { + const overId = over?.id; + + if (!overId || options.find(option => option.id === active.id)) { + return; + } + + const overContainer = findContainer(overId); + const activeContainer = findContainer(active.id); + + if (!overContainer || !activeContainer) { + return; + } + + if (activeContainer !== overContainer) { + const activeOption = options.find( + option => option.id === activeContainer + ); + const overOption = options.find(option => option.id === overContainer); + const activeActions = activeOption.decodedActions; + const overActions = overOption.decodedActions; + const overIndex = overActions.findIndex(item => item.id === overId); + const activeIndex = activeActions.findIndex( + item => item.id === active.id + ); + + let newIndex: number; + + if (options.find(option => option.id === overId)) { + newIndex = overActions.length + 1; + } else { + const isBelowOverItem = + over && + active.rect.current.translated && + active.rect.current.translated.top > over.rect.top + over.rect.height; + + const modifier = isBelowOverItem ? 1 : 0; + + newIndex = + overIndex >= 0 ? overIndex + modifier : overActions.length + 1; + } + + recentlyMovedToNewContainer.current = true; - function handleDragEnd(event: DragEndEvent) { - const { active, over } = event; + let activeAction: DecodedAction; + const updatedOptions = options + .map(option => { + if (option.id === activeContainer) { + activeAction = option.decodedActions[activeIndex]; + option.decodedActions = option.decodedActions.filter( + action => action.id !== active.id + ); + } + return option; + }) + .map(option => { + if (option.id === overContainer) { + const decodedActions = option.decodedActions; + option.decodedActions = [ + ...decodedActions.slice(0, newIndex), + activeAction, + ...decodedActions.slice(newIndex), + ]; + } + return option; + }); + onChange(updatedOptions); + } + } - if (active.id !== over.id) { + function handleDragEnd({ active, over }: DragEndEvent) { + if (options.find(option => option.id === active.id) && over?.id) { const items = [...options]; const oldIndex = items.findIndex(item => item.id === active.id); const newIndex = items.findIndex(item => item.id === over.id); onChange(arrayMove(items, oldIndex, newIndex)); } + + const activeContainer = findContainer(active.id); + + if (!activeContainer) { + setActiveId(null); + return; + } + + const overId = over?.id; + + if (!overId) { + setActiveId(null); + return; + } + + if (overId === PLACEHOLDER_ID) { + const newOptions = options.map(option => { + if (option.id === activeContainer) { + option.decodedActions = option.decodedActions.filter( + action => action.id === activeId + ); + } + return option; + }); + + newOptions.push({ + id: `option-${options.length}`, + label: `Option ${options.length + 1}`, + color: theme?.colors?.votes?.[options.length], + decodedActions: [], + }); + onChange(newOptions); + return; + } + + const overContainer = findContainer(overId); + + if (overContainer) { + const activeIndex = options + .find(option => option.id === activeContainer) + .decodedActions.findIndex(item => item.id === active.id); + const overIndex = options + .find(option => option.id === overContainer) + .decodedActions.findIndex(item => item.id === active.id); + + if (activeIndex !== overIndex) { + const newOptions = options.map(option => { + if (option.id === overContainer) { + option.decodedActions = arrayMove( + option.decodedActions, + activeIndex, + overIndex + ); + } + return option; + }); + + onChange(newOptions); + } + } + + setActiveId(null); } + const handleDragCancel = () => { + if (clonedOptions) { + // Reset items to their original state in case items have been + // Dragged across containers + onChange(clonedOptions); + } + + setActiveId(null); + setClonedOptions(null); + }; + + /** + * Custom collision detection strategy optimized for multiple containers + * + * - First, find any droppable containers intersecting with the pointer. + * - If there are none, find intersecting containers with the active draggable. + * - If there are no intersecting containers, return the last matched intersection + * + */ + const collisionDetectionStrategy: CollisionDetection = useCallback( + args => { + // Collision detection when dragging Options + if (activeId && options.find(option => option.id === activeId)) { + return closestCenter({ + ...args, + droppableContainers: args.droppableContainers.filter(container => + options.find(option => option.id === container.id) + ), + }); + } + + // Collision detection when dragging Actions + + // Start by finding any intersecting droppable + const pointerIntersections = pointerWithin(args); + const intersections = + pointerIntersections.length > 0 + ? // If there are droppables intersecting with the pointer, return those + pointerIntersections + : rectIntersection(args); + let overId = getFirstCollision(intersections, 'id'); + + if (overId != null) { + const overOption = options.find(option => option.id === overId); + if (overOption) { + const overOptionActions = overOption.decodedActions; + + // If a container is matched and it contains Actions + if (overOptionActions.length > 0) { + // Return the closest droppable within that container + overId = closestCenter({ + ...args, + droppableContainers: args.droppableContainers.filter( + container => + container.id !== overId && + overOptionActions.find(action => action.id === container.id) + ), + })[0]?.id; + } + } + + lastOverId.current = overId; + + return [{ id: overId }]; + } + + // When a draggable item moves to a new container, the layout may shift + // and the `overId` may become `null`. We manually set the cached `lastOverId` + // to the id of the draggable item that was moved to the new container, otherwise + // the previous `overId` will be returned which can cause items to incorrectly shift positions + if (recentlyMovedToNewContainer.current) { + lastOverId.current = activeId; + } + + // If no droppable is matched, return the last match + return lastOverId.current ? [{ id: lastOverId.current }] : []; + }, + [activeId, options] + ); + return ( - - + + {options?.map((option, idx) => ( <> = ({ if (!selectedAction || !data || !setSelectedActionContract) return; const decodedAction: DecodedAction = { + id: `action-${Math.random()}`, decodedCall: data, contract: selectedActionContract, }; diff --git a/src/hooks/Guilds/contracts/useDecodedCall.ts b/src/hooks/Guilds/contracts/useDecodedCall.ts index 5cbf224b72..db2809e765 100644 --- a/src/hooks/Guilds/contracts/useDecodedCall.ts +++ b/src/hooks/Guilds/contracts/useDecodedCall.ts @@ -117,6 +117,7 @@ const decodeCall = ( ); return { + id: `action-${Math.random()}`, decodedCall, contract: contractInterface, }; From f151d22a22284f8913e89ab2a6903a9646d99c26 Mon Sep 17 00:00:00 2001 From: Madusha Prasanjith Date: Thu, 7 Apr 2022 00:55:30 +0530 Subject: [PATCH 13/67] feat: Reorder actions by drag and drop. --- .../Guilds/ActionsBuilder/OptionsList.tsx | 38 +++---------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/OptionsList.tsx b/src/components/Guilds/ActionsBuilder/OptionsList.tsx index 8fa7b0777d..6743b285cc 100644 --- a/src/components/Guilds/ActionsBuilder/OptionsList.tsx +++ b/src/components/Guilds/ActionsBuilder/OptionsList.tsx @@ -38,8 +38,6 @@ interface OptionsListProps { onChange: (options: Option[]) => void; } -const PLACEHOLDER_ID = 'placeholder'; - const OptionsList: React.FC = ({ isEditable, options, @@ -164,6 +162,7 @@ const OptionsList: React.FC = ({ } function handleDragEnd({ active, over }: DragEndEvent) { + // Reorder options if (options.find(option => option.id === active.id) && over?.id) { const items = [...options]; const oldIndex = items.findIndex(item => item.id === active.id); @@ -172,48 +171,24 @@ const OptionsList: React.FC = ({ } const activeContainer = findContainer(active.id); - if (!activeContainer) { setActiveId(null); return; } - const overId = over?.id; - - if (!overId) { + const overContainer = over?.id ? findContainer(over?.id) : null; + if (!overContainer) { setActiveId(null); return; } - if (overId === PLACEHOLDER_ID) { - const newOptions = options.map(option => { - if (option.id === activeContainer) { - option.decodedActions = option.decodedActions.filter( - action => action.id === activeId - ); - } - return option; - }); - - newOptions.push({ - id: `option-${options.length}`, - label: `Option ${options.length + 1}`, - color: theme?.colors?.votes?.[options.length], - decodedActions: [], - }); - onChange(newOptions); - return; - } - - const overContainer = findContainer(overId); - if (overContainer) { const activeIndex = options .find(option => option.id === activeContainer) .decodedActions.findIndex(item => item.id === active.id); const overIndex = options .find(option => option.id === overContainer) - .decodedActions.findIndex(item => item.id === active.id); + .decodedActions.findIndex(item => item.id === over.id); if (activeIndex !== overIndex) { const newOptions = options.map(option => { @@ -328,10 +303,7 @@ const OptionsList: React.FC = ({ onDragEnd={handleDragEnd} onDragCancel={handleDragCancel} > - + {options?.map((option, idx) => ( <> Date: Thu, 7 Apr 2022 02:12:29 +0530 Subject: [PATCH 14/67] feat: Improve drag and drop styles. --- .../Guilds/ActionsBuilder/Action.tsx | 25 +++++++++++++-- .../Guilds/ActionsBuilder/Option.tsx | 32 +++++++++++++++---- .../Guilds/ActionsBuilder/OptionsList.tsx | 11 +++++-- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/Action.tsx b/src/components/Guilds/ActionsBuilder/Action.tsx index a7fdb684ef..eb4ce58bac 100644 --- a/src/components/Guilds/ActionsBuilder/Action.tsx +++ b/src/components/Guilds/ActionsBuilder/Action.tsx @@ -14,7 +14,15 @@ import EditButton from './common/EditButton'; import { useSortable } from '@dnd-kit/sortable'; const CardWrapperWithMargin = styled(CardWrapper)` + position: relative; + background-color: ${({ theme }) => theme.colors.background}; margin-top: 0.8rem; + border: 1px solid; + border-color: ${({ dragging, theme }) => + dragging ? theme.colors.text : theme.colors.muted}; + z-index: ${({ dragging }) => (dragging ? 999 : 'initial')}; + box-shadow: ${({ dragging }) => + dragging ? '0px 4px 8px 0px rgba(0, 0, 0, 0.2)' : 'none'}; `; const CardHeader = styled(Header)` @@ -94,8 +102,14 @@ const ActionRow: React.FC = ({ decodedAction, isEditable, }) => { - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ id: decodedAction?.id }); + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id: decodedAction?.id, disabled: !isEditable }); const { decodedCall: decodedCallFromCall } = useDecodedCall(call); @@ -114,7 +128,12 @@ const ActionRow: React.FC = ({ }; return ( - + {isEditable && } diff --git a/src/components/Guilds/ActionsBuilder/Option.tsx b/src/components/Guilds/ActionsBuilder/Option.tsx index efab9267b2..82bce0a626 100644 --- a/src/components/Guilds/ActionsBuilder/Option.tsx +++ b/src/components/Guilds/ActionsBuilder/Option.tsx @@ -18,7 +18,16 @@ import { } from '@dnd-kit/sortable'; export const OptionWrapper = styled(Box)` + position: relative; + background-color: ${({ theme }) => theme.colors.background}; padding: 1rem; + border-top: 1px solid; + border-bottom: 1px solid; + border-color: ${({ dragging, theme }) => + dragging ? theme.colors.text : 'transparent'}; + z-index: ${({ dragging }) => (dragging ? 999 : 'initial')}; + box-shadow: ${({ dragging }) => + dragging ? '0px 4px 8px 0px rgba(0, 0, 0, 0.2)' : 'none'}; `; export const DetailWrapper = styled(Box)` @@ -48,8 +57,14 @@ const OptionRow: React.FC = ({ option, onChange, }) => { - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ id: option.id }); + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id: option.id }); const [isActionsModalOpen, setIsActionsModalOpen] = useState(false); function addAction(action: DecodedAction) { @@ -72,7 +87,12 @@ const OptionRow: React.FC = ({ }; return ( - +
{isEditable && ( @@ -85,8 +105,8 @@ const OptionRow: React.FC = ({ - {option?.actions?.length || 'No'} on-chain{' '} - {option?.actions?.length >= 2 ? 'actions' : 'action'} + {option?.decodedActions?.length || 'No'} on-chain{' '} + {option?.decodedActions?.length >= 2 ? 'actions' : 'action'}
@@ -100,7 +120,7 @@ const OptionRow: React.FC = ({ {!isEditable && option?.actions?.map((action, index) => ( - + ))} {isEditable && ( diff --git a/src/components/Guilds/ActionsBuilder/OptionsList.tsx b/src/components/Guilds/ActionsBuilder/OptionsList.tsx index 6743b285cc..afb900548e 100644 --- a/src/components/Guilds/ActionsBuilder/OptionsList.tsx +++ b/src/components/Guilds/ActionsBuilder/OptionsList.tsx @@ -27,6 +27,10 @@ import OptionRow from './Option'; import AddButton from './common/AddButton'; import { DecodedAction, Option } from './types'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { + restrictToVerticalAxis, + restrictToFirstScrollableAncestor, +} from '@dnd-kit/modifiers'; const AddOptionWrapper = styled(Box)` padding: 1rem; @@ -105,6 +109,7 @@ const OptionsList: React.FC = ({ return; } + // When dragging an action between options, move the action over to the new option if (activeContainer !== overContainer) { const activeOption = options.find( option => option.id === activeContainer @@ -182,6 +187,7 @@ const OptionsList: React.FC = ({ return; } + // Reorder actions if (overContainer) { const activeIndex = options .find(option => option.id === activeContainer) @@ -230,7 +236,7 @@ const OptionsList: React.FC = ({ */ const collisionDetectionStrategy: CollisionDetection = useCallback( args => { - // Collision detection when dragging Options + // --- Collision detection when dragging Options if (activeId && options.find(option => option.id === activeId)) { return closestCenter({ ...args, @@ -240,7 +246,7 @@ const OptionsList: React.FC = ({ }); } - // Collision detection when dragging Actions + // --- Collision detection when dragging Actions // Start by finding any intersecting droppable const pointerIntersections = pointerWithin(args); @@ -297,6 +303,7 @@ const OptionsList: React.FC = ({ strategy: MeasuringStrategy.Always, }, }} + modifiers={[restrictToVerticalAxis, restrictToFirstScrollableAncestor]} collisionDetection={collisionDetectionStrategy} onDragStart={handleDragStart} onDragOver={handleDragOver} From 4577f5a64ffdf5d3a8d771ca1e75a32cad122c54 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 7 Apr 2022 13:24:58 -0300 Subject: [PATCH 15/67] fix(scripts/dev): small changes ot fix running dev script --- hardhat.config.js | 2 +- scripts/dev.ts | 2 +- yarn.lock | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hardhat.config.js b/hardhat.config.js index 61dfd9591e..dc22195a4b 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -21,9 +21,9 @@ module.exports = { 'dxdao-contracts/contracts/dxdao/DxToken.sol', 'dxdao-contracts/contracts/dxvote/DXDVotingMachine.sol', 'dxdao-contracts/contracts/dxvote/WalletScheme.sol', - 'dxdao-contracts/contracts/dxvote/PermissionRegistry.sol', 'dxdao-contracts/contracts/dxvote/utils/DXDVestingFactory.sol', 'dxdao-contracts/contracts/dxvote/utils/DXdaoNFT.sol', + 'dxdao-contracts/contracts/utils/PermissionRegistry.sol', 'dxdao-contracts/contracts/utils/Multicall.sol', 'dxdao-contracts/contracts/test/ERC20Mock.sol', 'dxdao-contracts/contracts/daostack/universalSchemes/ContributionReward.sol', diff --git a/scripts/dev.ts b/scripts/dev.ts index 964681f36a..0621216b10 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -318,7 +318,7 @@ async function main() { to: ['PermissionRegistry'], callData: [ new web3.eth.Contract(PermissionRegistry.abi).methods - .setAdminPermission( + .setPermission( ZERO_ADDRESS, '0xE0FC07f3aC4F6AF1463De20eb60Cf1A764E259db', '0x1A0370A6f5b6cE96B1386B208a8519552eb714D9', diff --git a/yarn.lock b/yarn.lock index 801e022b56..f8d36d9654 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3142,15 +3142,15 @@ find-up "^4.1.0" fs-extra "^8.1.0" -"@openzeppelin/contracts-upgradeable@4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.3.2.tgz#92df481362e366c388fc02133cf793029c744cea" - integrity sha512-i/pOaOtcqDk4UqsrOv735uYyTbn6dvfiuVu5hstsgV6c4ZKUtu88/31zT2BzkCg+3JfcwOfgg2TtRKVKKZIGkQ== +"@openzeppelin/contracts-upgradeable@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.4.0.tgz#85161d87c840c5bce2b6ed0c727b407e774852ae" + integrity sha512-hIEyWJHu7bDTv6ckxOaV+K3+7mVzhjtyvp3QSaz56Rk5PscXtPAbkiNTb3yz6UJCWHPWpxVyULVgZ6RubuFEZg== -"@openzeppelin/contracts@4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.2.tgz#ff80affd6d352dbe1bbc5b4e1833c41afd6283b6" - integrity sha512-AybF1cesONZStg5kWf6ao9OlqTZuPqddvprc0ky7lrUVOjXeKpmQ2Y9FK+6ygxasb+4aic4O5pneFBfwVsRRRg== +"@openzeppelin/contracts@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.4.0.tgz#4a1df71f736c31230bbbd634dfb006a756b51e6b" + integrity sha512-dlKiZmDvJnGRLHojrDoFZJmsQVeltVeoiRN7RK+cf2FmkhASDEblE0RiaYdxPNsUZa6mRG8393b9bfyp+V5IAw== "@openzeppelin/test-helpers@^0.5.15": version "0.5.15" @@ -9056,11 +9056,11 @@ duplexify@^3.4.2, duplexify@^3.6.0: "dxdao-contracts@https://github.com/DXGovernance/dxdao-contracts.git#develop": version "0.0.1" - resolved "https://github.com/DXGovernance/dxdao-contracts.git#4361fd7270d275f8d046471b4bbfa3b1062d15ab" + resolved "https://github.com/DXGovernance/dxdao-contracts.git#fe74c569b0721a23e1975f714aa324181381bab3" dependencies: "@maticnetwork/eth-decoder" "^0.0.4" - "@openzeppelin/contracts" "4.3.2" - "@openzeppelin/contracts-upgradeable" "4.3.2" + "@openzeppelin/contracts" "4.4.0" + "@openzeppelin/contracts-upgradeable" "4.4.0" "@openzeppelin/test-helpers" "^0.5.15" "@realitio/realitio-contracts" ">=0.0.0" "@truffle/hdwallet-provider" "^1.4.0" From c24b884d70d57ab1b0e9249dfdaf966df48dde96 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 7 Apr 2022 13:59:22 -0300 Subject: [PATCH 16/67] fix(scripts/dev): update dxdao-contracts dependency and change dapp to work with latest dev script --- src/services/CacheService.ts | 49 ++++++++++++++++++------------------ src/stores/DaoStore.ts | 2 +- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 61f15487e5..b2e6cab528 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -835,7 +835,26 @@ export default class UtilsService { schemeType = 'Wallet Scheme v1.0'; switch (schemeType) { - case 'Wallet Scheme v1.1': + case 'Wallet Scheme v1.0': + callsToExecute.push([walletSchemeContract, 'votingMachine', []]); + callsToExecute.push([ + walletSchemeContract, + 'controllerAddress', + [], + ]); + callsToExecute.push([walletSchemeContract, 'schemeName', []]); + callsToExecute.push([ + walletSchemeContract, + 'maxSecondsForExecution', + [], + ]); + callsToExecute.push([ + walletSchemeContract, + 'maxRepPercentageChange', + [], + ]); + break; + default: const walletSchemeContract1_1 = await new web3.eth.Contract( WalletScheme1_1JSON.abi, schemeAddress @@ -862,28 +881,8 @@ export default class UtilsService { [], ]); break; - default: - callsToExecute.push([walletSchemeContract, 'votingMachine', []]); - callsToExecute.push([ - walletSchemeContract, - 'controllerAddress', - [], - ]); - callsToExecute.push([walletSchemeContract, 'schemeName', []]); - callsToExecute.push([ - walletSchemeContract, - 'maxSecondsForExecution', - [], - ]); - callsToExecute.push([ - walletSchemeContract, - 'maxRepPercentageChange', - [], - ]); - break; } } - const callsResponse1 = await executeMulticall( web3, networkWeb3Contracts.multicall, @@ -905,14 +904,14 @@ export default class UtilsService { if (schemeTypeData.type === 'WalletScheme') { switch (schemeType) { - case 'Wallet Scheme v1.1': + case 'Wallet Scheme v1.0': + controllerAddress = callsResponse1.decodedReturnData[3]; + break; + default: controllerAddress = callsResponse1.decodedReturnData[3] ? networkWeb3Contracts.controller._address : ZERO_ADDRESS; break; - default: - controllerAddress = callsResponse1.decodedReturnData[3]; - break; } schemeName = callsResponse1.decodedReturnData[4]; maxSecondsForExecution = callsResponse1.decodedReturnData[5]; diff --git a/src/stores/DaoStore.ts b/src/stores/DaoStore.ts index 4a953584d5..3c9b254e93 100644 --- a/src/stores/DaoStore.ts +++ b/src/stores/DaoStore.ts @@ -59,7 +59,7 @@ export default class DaoStore { ].value = bnum( unparsedCache.callPermissions[asset][from][to][ functionSignature - ].value + ].value || 0 ); } ); From 95ea5638fa92033d9988787cf18f8b61296680c8 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 7 Apr 2022 14:10:04 -0300 Subject: [PATCH 17/67] fix(services/cacheservice): use getRawEvents instead of getPastLogs --- src/services/CacheService.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index b2e6cab528..32665493b6 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1281,15 +1281,17 @@ export default class UtilsService { ); } else { if (schemeTypeData.type === 'GenericMulticall') { - const executionEvent = await web3.eth.getPastLogs({ - fromBlock: schemeEvent.blockNumber, - address: schemeAddress, - topics: [ + const executionEvent = await getRawEvents( + web3, + schemeAddress, + schemeEvent.blockNumber, + toBlock, + [ '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', avatarAddressEncoded, proposalId, - ], - }); + ] + ); if (executionEvent.length > 0) schemeProposalInfo.state = WalletSchemeProposalState.ExecutionSucceded; @@ -1889,17 +1891,17 @@ export default class UtilsService { } } } else if (schemeTypeData.type === 'GenericMulticall') { - const executionEvent = await web3.eth.getPastLogs({ - fromBlock: - networkCache.proposals[proposal.id].creationEvent - .blockNumber, - address: schemeAddress, - topics: [ + const executionEvent = await await getRawEvents( + web3, + schemeAddress, + networkCache.proposals[proposal.id].creationEvent.blockNumber, + toBlock, + [ '0x6bc0cb9e9967b59a69ace442598e1df4368d38661bd5c0800fbcbc9fe855fbbe', avatarAddressEncoded, proposal.id, - ], - }); + ] + ); if (executionEvent.length > 0) networkCache.proposals[proposal.id].stateInScheme = WalletSchemeProposalState.ExecutionSucceded; From e2a61ddc445591ef1f943120417ae2d5cf896c66 Mon Sep 17 00:00:00 2001 From: Milton Tulli Date: Fri, 8 Apr 2022 11:36:27 -0300 Subject: [PATCH 18/67] Feature/vote calculation for snapshot implementation (#776) * Refactor useGuildConfig to use diff guild implementations returning totalLocked value * format * Remove logs * create useVotingPowerOfAt hook and refactor * Add json contracts, pull out token from guild config by creating useGuildToken hook, create a useTotalSupplyAt hook. * fix return data of useGuildConfig * remove comments --- scripts/dev.ts | 142 +- .../ProposalPage/ProposalVoteCard/index.tsx | 1 - src/contracts/ERC20SnapshotRep.json | 459 +++++++ src/contracts/SnapshotERC20Guild.json | 1145 ++++++++++++++++ src/contracts/SnapshotRepERC20Guild.json | 1146 +++++++++++++++++ .../Guilds/ether-swr/guild/useGuildConfig.ts | 21 +- .../Guilds/ether-swr/guild/useGuildToken.ts | 11 + .../Guilds/ether-swr/guild/useSnapshotId.ts | 24 + .../Guilds/ether-swr/guild/useTotalLocked.ts | 51 + .../ether-swr/guild/useTotalLockedAt.ts | 32 + .../ether-swr/guild/useTotalSupplyAt.ts | 32 + .../ether-swr/guild/useVotingPowerOfAt.ts | 31 + .../guild/useGuildImplementationType.ts | 9 +- 13 files changed, 3055 insertions(+), 49 deletions(-) create mode 100644 src/contracts/ERC20SnapshotRep.json create mode 100644 src/contracts/SnapshotERC20Guild.json create mode 100644 src/contracts/SnapshotRepERC20Guild.json create mode 100644 src/hooks/Guilds/ether-swr/guild/useGuildToken.ts create mode 100644 src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts create mode 100644 src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts create mode 100644 src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts create mode 100644 src/hooks/Guilds/ether-swr/guild/useTotalSupplyAt.ts create mode 100644 src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts diff --git a/scripts/dev.ts b/scripts/dev.ts index 0621216b10..112b4cb9bb 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -9,23 +9,47 @@ import { const hre = require('hardhat'); const moment = require('moment'); +const accounts = [ + '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + '0xc73480525e9d1198d448ece4a01daea851f72a9d', + '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + '0xaf1a6415202453d79b84d8a9d055b8f9141f696b', + '0x02803e2cdff171d1910d178dac948c711937bd3f', + '0x797c62953ef866a1ece855c4033cc5dc3c11290b', + '0x016f70459e4ba98e0d60a5f5855e117e8ff39cae', + '0x073f4fdc12f805b8d98e58551fc10d0a71bbc7db', + '0x6829556f30899d70403947590ffe8900e8c0d0d7', + '0x2b410bcb3b8096164fa8c6a06220a66bfb77058d', + '0x309f75c54a57937a7a0c6eb9b36eb1dbca82407e', + '0xec9d2d34ad6acda19ab8afe71595051b206e3e4d', + '0x40c23c536bad1fe206ce911114f2c70309a7e487', + '0x28d254f2ddb522c43a21d338e337fd8d2f820db2', + '0xaf7386ce842cc0cffef91361059b0ca9ae48d6a0', + '0x46c18451aaead6a2cb888b5bd6193c0f2c402329', + '0xc707c8143a6e1274ae7f637946f685870925261f', + '0x5b14a88dbbb04abcb6e5bf6384491be8d939cf57', + '0x92d356240dda25d050aa441690b92b2fa0011b84', + '0x5a485c203d9537095a6be2acc5a7ad83805d301d', +]; + async function main() { const web3 = hre.web3; const PermissionRegistry = await hre.artifacts.require('PermissionRegistry'); const ERC20Guild = await hre.artifacts.require('ERC20Guild'); + const SnapshotERC20Guild = await hre.artifacts.require('SnapshotERC20Guild'); const deployconfig = { reputation: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: 6000, }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: 4000, }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: 1000, }, ], @@ -36,15 +60,15 @@ async function main() { symbol: 'DXD', distribution: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: web3.utils.toWei('220'), }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: web3.utils.toWei('50'), }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: web3.utils.toWei('10'), }, ], @@ -54,15 +78,15 @@ async function main() { symbol: 'RGT', distribution: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: web3.utils.toWei('200'), }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: web3.utils.toWei('50'), }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: web3.utils.toWei('10'), }, ], @@ -72,15 +96,15 @@ async function main() { symbol: 'SGT', distribution: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: web3.utils.toWei('200'), }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: web3.utils.toWei('40'), }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: web3.utils.toWei('10'), }, ], @@ -248,7 +272,7 @@ async function main() { { token: 'SGT', contractName: 'SnapshotERC20Guild', - name: 'SnapshotGuild', + name: 'SnapshotERC20Guild', proposalTime: moment.duration(5, 'minutes').asSeconds(), timeForExecution: moment.duration(2, 'minutes').asSeconds(), votingPowerForProposalExecution: '50', @@ -265,7 +289,7 @@ async function main() { actions: [ { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: ZERO_ADDRESS, address: 'Avatar', @@ -274,7 +298,7 @@ async function main() { }, { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: 'DXD', address: 'Avatar', @@ -284,7 +308,7 @@ async function main() { { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: ZERO_ADDRESS, address: 'DXDGuild', @@ -293,7 +317,7 @@ async function main() { }, { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: 'DXD', address: 'DXDGuild', @@ -303,7 +327,26 @@ async function main() { { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[1], + data: { + asset: ZERO_ADDRESS, + address: 'SnapshotERC20Guild', + amount: web3.utils.toWei('10'), + }, + }, + { + type: 'transfer', + from: accounts[1], + data: { + asset: 'SGT', + address: 'SnapshotERC20Guild', + amount: web3.utils.toWei('10'), + }, + }, + + { + type: 'transfer', + from: accounts[0], data: { asset: ZERO_ADDRESS, address: 'REPGuild', @@ -313,7 +356,7 @@ async function main() { { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { to: ['PermissionRegistry'], callData: [ @@ -337,7 +380,7 @@ async function main() { }, { type: 'stake', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', decision: '1', @@ -347,7 +390,7 @@ async function main() { { type: 'vote', time: moment.duration(1, 'minutes').asSeconds(), - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', decision: '1', @@ -357,14 +400,14 @@ async function main() { { type: 'execute', time: moment.duration(3, 'minutes').asSeconds(), - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', }, }, { type: 'redeem', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', }, @@ -372,7 +415,7 @@ async function main() { { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { to: ['QuickWalletScheme'], callData: ['0x0'], @@ -385,7 +428,7 @@ async function main() { }, { type: 'stake', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '1', decision: '1', @@ -395,7 +438,7 @@ async function main() { { type: 'vote', time: moment.duration(1, 'minutes').asSeconds(), - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '1', decision: '1', @@ -404,7 +447,7 @@ async function main() { }, { type: 'vote', - from: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + from: accounts[1], data: { proposal: '1', decision: '2', @@ -414,9 +457,9 @@ async function main() { { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { - to: ['0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351'], + to: [accounts[2]], callData: ['0x0'], value: [web3.utils.toWei('1.5')], title: 'Proposal Test #2', @@ -429,7 +472,7 @@ async function main() { { type: 'approve', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: 'DXD', address: 'DXDGuild-vault', @@ -438,7 +481,7 @@ async function main() { }, { type: 'guild-lockTokens', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { guildName: 'DXDGuild', amount: web3.utils.toWei('100'), @@ -446,7 +489,7 @@ async function main() { }, { type: 'guild-withdrawTokens', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { guildName: 'DXDGuild', amount: web3.utils.toWei('10'), @@ -455,7 +498,7 @@ async function main() { }, { type: 'guild-createProposal', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { guildName: 'DXDGuild', to: ['DXDGuild'], @@ -479,7 +522,7 @@ async function main() { }, { type: 'guild-voteProposal', - from: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + from: accounts[1], data: { guildName: 'DXDGuild', proposal: 0, @@ -490,18 +533,43 @@ async function main() { { time: moment.duration(10, 'minutes').asSeconds(), type: 'guild-endProposal', - from: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + from: accounts[1], data: { guildName: 'DXDGuild', proposal: 0, }, }, + { + type: 'guild-createProposal', + from: accounts[1], + data: { + guildName: 'SnapshotERC20Guild', + to: ['SnapshotERC20Guild'], + callData: [ + new web3.eth.Contract(SnapshotERC20Guild.abi).methods + .setPermission( + [ZERO_ADDRESS], + [ANY_ADDRESS], + [ANY_FUNC_SIGNATURE], + [web3.utils.toWei('5').toString()], + [true] + ) + .encodeABI(), + ], + value: ['0'], + totalActions: '1', + title: 'Proposal Test #1 to SnapshotERC20Guild', + description: + 'Allow call any address and function and send a max of 5 ETH per proposal', + }, + }, + { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { - to: ['0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351'], + to: [accounts[2]], callData: ['0x0'], value: [web3.utils.toWei('1.5')], title: 'Proposal Test #3', diff --git a/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx b/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx index b84009e26e..992058c69f 100644 --- a/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx +++ b/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx @@ -101,7 +101,6 @@ const ProposalVoteCard = () => { contract.setVote(proposalId, selectedAction, userVotingPower) ); }; - return ( { +export const useGuildConfig = ( + guildAddress: string +): SWRResponse => { const { data, error, isValidating, mutate } = useEtherSWR( guildAddress ? [ - [guildAddress, 'getToken'], // Get the address of the ERC20Token used for voting [guildAddress, 'getPermissionRegistry'], // Get the address of the permission registry contract [guildAddress, 'getName'], // Get the name of the ERC20Guild [guildAddress, 'getProposalTime'], // Get the proposalTime (seconds) @@ -31,7 +35,6 @@ export const useGuildConfig = (guildAddress: string) => { [guildAddress, 'getVotingPowerForProposalExecution'], [guildAddress, 'getTokenVault'], [guildAddress, 'getLockTime'], - [guildAddress, 'getTotalLocked'], ] : [], { @@ -39,13 +42,14 @@ export const useGuildConfig = (guildAddress: string) => { refreshInterval: 0, } ); + const { data: token } = useGuildToken(guildAddress); + const { data: totalLocked } = useTotalLocked(guildAddress); // TODO: Move this into a SWR middleware - const transformedData: GuildConfig = useMemo(() => { + const transformedData = useMemo(() => { if (!data) return undefined; const [ - token, permissionRegistry, name, proposalTime, @@ -55,11 +59,9 @@ export const useGuildConfig = (guildAddress: string) => { votingPowerForProposalExecution, tokenVault, lockTime, - totalLocked, ] = data; return { - token, permissionRegistry, name, proposalTime: BigNumber.from(proposalTime), @@ -73,7 +75,6 @@ export const useGuildConfig = (guildAddress: string) => { ), tokenVault, lockTime: BigNumber.from(lockTime), - totalLocked: BigNumber.from(totalLocked), }; }, [data]); @@ -81,6 +82,8 @@ export const useGuildConfig = (guildAddress: string) => { error, isValidating, mutate, - data: transformedData, + data: transformedData + ? { ...transformedData, totalLocked, token } + : undefined, }; }; diff --git a/src/hooks/Guilds/ether-swr/guild/useGuildToken.ts b/src/hooks/Guilds/ether-swr/guild/useGuildToken.ts new file mode 100644 index 0000000000..fbc6617b1b --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useGuildToken.ts @@ -0,0 +1,11 @@ +import useEtherSWR from '../useEtherSWR'; +import ERC20Guild from 'contracts/ERC20Guild.json'; + +const useGuildToken = (guildAddress: string) => { + return useEtherSWR(guildAddress ? [guildAddress, 'getToken'] : [], { + ABIs: new Map([[guildAddress, ERC20Guild.abi]]), + refreshInterval: 0, + }); +}; + +export default useGuildToken; diff --git a/src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts b/src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts new file mode 100644 index 0000000000..0b8f20e520 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts @@ -0,0 +1,24 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +import SnapshotERC20Guild from 'contracts/SnapshotERC20Guild.json'; + +interface UseSnapshotIdProps { + contractAddress: string; + proposalId: string; +} + +type UseSnapshotIdHook = (args: UseSnapshotIdProps) => SWRResponse; + +const useSnapshotId: UseSnapshotIdHook = ({ contractAddress, proposalId }) => { + return useEtherSWR( + proposalId && contractAddress + ? [contractAddress, 'getProposalSnapshotId', proposalId] + : [], + { + ABIs: new Map([[contractAddress, SnapshotERC20Guild.abi]]), + } + ); +}; + +export default useSnapshotId; diff --git a/src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts b/src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts new file mode 100644 index 0000000000..6f24e4d412 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts @@ -0,0 +1,51 @@ +import { useParams } from 'react-router-dom'; +import ERC20GuildContract from 'contracts/ERC20Guild.json'; +import useEtherSWR from '../useEtherSWR'; +import useTotalLockedAt from 'hooks/Guilds/ether-swr/guild/useTotalLockedAt'; +import useSnapshotId from 'hooks/Guilds/ether-swr/guild/useSnapshotId'; +import useGuildImplementationType from 'hooks/Guilds/guild/useGuildImplementationType'; +import useGuildToken from './useGuildToken'; +import useTotalSupplyAt from './useTotalSupplyAt'; + +const useTotalLocked = (guildAddress: string, snapshotId?: string) => { + // Hooks call + const { proposal_id: proposalId } = useParams<{ proposal_id?: string }>(); + + const { data: _snapshotId } = useSnapshotId({ + contractAddress: guildAddress, + proposalId, + }); + + const SNAPSHOT_ID = snapshotId ? snapshotId : _snapshotId?.toString(); + + const { isSnapshotGuild, isRepGuild, isSnapshotRepGuild } = + useGuildImplementationType(guildAddress); + + const totalLockedResponse = useEtherSWR( + guildAddress ? [guildAddress, 'getTotalLocked'] : [], + { + ABIs: new Map([[guildAddress, ERC20GuildContract.abi]]), + refreshInterval: 0, + } + ); + + const totalLockedAtProposalSnapshotResponse = useTotalLockedAt({ + contractAddress: guildAddress, + snapshotId: SNAPSHOT_ID, + }); + + const { data: guildTokenAddress } = useGuildToken(guildAddress); + + const totalSupplyAtSnapshotResponse = useTotalSupplyAt({ + contractAddress: guildTokenAddress, + snapshotId: SNAPSHOT_ID, + }); + + // Return response based on implementation type + if (isSnapshotGuild) return totalLockedAtProposalSnapshotResponse; + if (isSnapshotRepGuild) return totalSupplyAtSnapshotResponse; + if (isRepGuild) return totalLockedResponse; + return totalLockedResponse; +}; + +export default useTotalLocked; diff --git a/src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts b/src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts new file mode 100644 index 0000000000..4a732a1562 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts @@ -0,0 +1,32 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +import SnapshotERC20Guild from 'contracts/SnapshotERC20Guild.json'; + +interface UseTotalLockedAtProps { + contractAddress: string; + snapshotId: string; +} + +type UseTotalLockedAtHook = ( + args: UseTotalLockedAtProps +) => SWRResponse; + +/** + * Get the total locked amount at snapshot + */ +const useTotalLockedAt: UseTotalLockedAtHook = ({ + contractAddress, + snapshotId, +}) => { + return useEtherSWR( + snapshotId && contractAddress + ? [contractAddress, 'totalLockedAt', snapshotId] + : [], + { + ABIs: new Map([[contractAddress, SnapshotERC20Guild.abi]]), + } + ); +}; + +export default useTotalLockedAt; diff --git a/src/hooks/Guilds/ether-swr/guild/useTotalSupplyAt.ts b/src/hooks/Guilds/ether-swr/guild/useTotalSupplyAt.ts new file mode 100644 index 0000000000..2f1f913aa6 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useTotalSupplyAt.ts @@ -0,0 +1,32 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +import ERC20SnapshotRep from 'contracts/ERC20SnapshotRep.json'; + +interface UseTotalSupplyAtProps { + contractAddress: string; + snapshotId: string; +} + +type UseTotalSupplyAtHook = ( + args: UseTotalSupplyAtProps +) => SWRResponse; + +/** + * Get the total supply amount at snapshot + */ +const useTotalSupplyAt: UseTotalSupplyAtHook = ({ + contractAddress, // tokenAddress, + snapshotId, +}) => { + return useEtherSWR( + snapshotId && contractAddress + ? [contractAddress, 'totalSupplyAt', snapshotId] + : [], + { + ABIs: new Map([[contractAddress, ERC20SnapshotRep.abi]]), + } + ); +}; + +export default useTotalSupplyAt; diff --git a/src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts b/src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts new file mode 100644 index 0000000000..bb979b1290 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts @@ -0,0 +1,31 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +import SnapshotERC20Guild from 'contracts/SnapshotERC20Guild.json'; + +interface useVotingPowerOfAtProps { + contractAddress: string; + userAddress: string; + snapshotId: string; +} + +type useVotingPowerOfAtHook = ( + args: useVotingPowerOfAtProps +) => SWRResponse; + +/** + * Get the voting power of an account at snapshot id + */ +export const useVotingPowerOfAt: useVotingPowerOfAtHook = ({ + contractAddress, + userAddress, + snapshotId, +}) => + useEtherSWR( + contractAddress && snapshotId && userAddress + ? [contractAddress, 'votingPowerOf', userAddress, snapshotId] + : [], + { + ABIs: new Map([[contractAddress, SnapshotERC20Guild.abi]]), + } + ); diff --git a/src/hooks/Guilds/guild/useGuildImplementationType.ts b/src/hooks/Guilds/guild/useGuildImplementationType.ts index 1ccd2e2d2e..4098514539 100644 --- a/src/hooks/Guilds/guild/useGuildImplementationType.ts +++ b/src/hooks/Guilds/guild/useGuildImplementationType.ts @@ -21,14 +21,19 @@ interface ImplementationTypeConfig { interface ImplementationTypeConfigReturn extends ImplementationTypeConfig { isRepGuild: boolean; isSnapshotGuild: boolean; + isSnapshotRepGuild: boolean; } const parseConfig = ( config: ImplementationTypeConfig ): ImplementationTypeConfigReturn => { return { ...config, - isRepGuild: config.features.includes('REP'), - isSnapshotGuild: config.features.includes('SNAPSHOT'), + isRepGuild: + config.features.includes('REP') && !config.features.includes('SNAPSHOT'), + isSnapshotGuild: + config.features.includes('SNAPSHOT') && !config.features.includes('REP'), + isSnapshotRepGuild: + config.features.includes('SNAPSHOT') && config.features.includes('REP'), }; }; From 27bc966d074af888c3bd6650c4b43646641a2375 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Sat, 9 Apr 2022 22:12:50 +0200 Subject: [PATCH 19/67] feat: started implementing REP mint functionality.UI is done --- src/assets/images/info.svg | 11 + src/assets/images/mint.svg | 3 + .../REPMint/REPMintEditor.tsx | 195 ++++++++++++++++++ .../REPMint/REPMintInfoLine.tsx | 67 ++++++ .../REPMint/REPMintSummary.tsx | 63 ++++++ .../ActionsBuilder/SupportedActions/index.tsx | 24 +++ src/components/Guilds/ActionsBuilder/types.ts | 1 + .../Guilds/ActionsModal/ContractsList.tsx | 9 + 8 files changed, 373 insertions(+) create mode 100644 src/assets/images/info.svg create mode 100644 src/assets/images/mint.svg create mode 100644 src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx create mode 100644 src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx create mode 100644 src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx diff --git a/src/assets/images/info.svg b/src/assets/images/info.svg new file mode 100644 index 0000000000..6826fa3923 --- /dev/null +++ b/src/assets/images/info.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/mint.svg b/src/assets/images/mint.svg new file mode 100644 index 0000000000..7ea9104747 --- /dev/null +++ b/src/assets/images/mint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx new file mode 100644 index 0000000000..6f7567c99e --- /dev/null +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -0,0 +1,195 @@ +import styled from 'styled-components'; +import { Input } from 'components/Guilds/common/Form/Input'; +import { FiX } from 'react-icons/fi'; +import Avatar from 'components/Guilds/Avatar'; +import { useWeb3React } from '@web3-react/core'; +import { useTokenList } from 'hooks/Guilds/tokens/useTokenList'; +import { useMemo } from 'react'; +import { ActionEditorProps } from '..'; +import { BigNumber, utils } from 'ethers'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; +import { Box } from 'components/Guilds/common/Layout'; +import { MAINNET_ID, shortenAddress } from 'utils'; +import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; +import { baseInputStyles } from 'components/Guilds/common/Form/Input'; +import { ReactComponent as Info } from '../../../../../assets/images/info.svg'; +import StyledIcon from 'components/Guilds/common/SVG'; + +const Control = styled(Box)` + display: flex; + flex-direction: column; + margin: 0.75rem 0; + width: 100%; +`; + +const ControlLabel = styled(Box)` + display: flex; + flex-direction: row; + margin-bottom: 0.75rem; + color: ${({ theme }) => theme.colors.proposalText.grey}; + font-size: ${({ theme }) => theme.fontSizes.body}; + font-weight: ${({ theme }) => theme.fontWeights.regular}; +`; + +const ControlRow = styled(Box)` + display: flex; + align-items: stretch; + height: 100%; +`; + +const ClickableIcon = styled(Box)` + display: flex; + align-items: center; + cursor: pointer; +`; + +const RepMintInput = styled(NumericalInput)` + ${baseInputStyles} + display: flex; + align-items: center; + width: 100%; + &:hover, + &:focus { + border: 0.1rem solid ${({ theme }) => theme.colors.text}; + } +`; + +interface TransferState { + source: string; + tokenAddress: string; + amount: BigNumber; + destination: string; +} + +const Mint: React.FC = ({ decodedCall, updateCall }) => { + const { chainId } = useWeb3React(); + + // parse transfer state from calls + const parsedData = useMemo(() => { + if (!decodedCall) return null; + + return { + source: decodedCall.from, + tokenAddress: decodedCall.to, + amount: decodedCall.args._value, + destination: decodedCall.args._to, + }; + }, [decodedCall]); + + const validations = useMemo(() => { + return { + tokenAddress: utils.isAddress(parsedData?.tokenAddress), + amount: BigNumber.isBigNumber(parsedData?.amount), + destination: utils.isAddress(parsedData?.destination), + }; + }, [parsedData]); + + // // Get token details from the token address + const { tokens } = useTokenList(chainId); + const token = useMemo(() => { + if (!parsedData?.tokenAddress || !tokens) return null; + + return tokens.find(({ address }) => address === parsedData.tokenAddress); + }, [tokens, parsedData]); + console.log(token); + + const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); + const roundedBalance = useBigNumberToNumber( + parsedData?.amount, + tokenInfo?.decimals, + 10 + ); + const { ensName, imageUrl: destinationAvatarUrl } = useENSAvatar( + parsedData?.destination, + MAINNET_ID + ); + + const setTransferAddress = (walletAddress: string) => { + updateCall({ + ...decodedCall, + args: { + ...decodedCall.args, + _to: walletAddress, + }, + }); + }; + + const setAmount = (value: string) => { + const amount = value + ? utils.parseUnits(value, tokenInfo?.decimals || 18) + : null; + updateCall({ + ...decodedCall, + args: { + ...decodedCall.args, + _value: amount, + }, + }); + }; + + return ( +
+ + + Recipient + + + + + {validations.destination && ( + <> + + + {ensName || parsedData?.destination + ? shortenAddress(parsedData?.destination) + : ''} + + + )} +
+ } + iconRight={ + parsedData?.destination ? ( + setTransferAddress('')}> + + + ) : null + } + onChange={e => setTransferAddress(e.target.value)} + /> + + + + + + Reputation in % + + + + + + + + + + Reputation Amount + + + + + + +
+ ); +}; + +export default Mint; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx new file mode 100644 index 0000000000..3cfb137ae8 --- /dev/null +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx @@ -0,0 +1,67 @@ +import Avatar from 'components/Guilds/Avatar'; +import { BigNumber } from 'ethers'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import { useMemo } from 'react'; +import { FiArrowRight } from 'react-icons/fi'; +import { MAINNET_ID, shortenAddress } from 'utils'; +import { ActionViewProps } from '..'; +import { Segment } from '../common/infoLine'; +import { ReactComponent as Mint } from '../../../../../assets/images/mint.svg'; +import StyledIcon from 'components/Guilds/common/SVG'; +import styled from 'styled-components'; + +const StyledMintIcon = styled(StyledIcon)` + margin: 0; +`; + +const REPMintInfoLine: React.FC = ({ decodedCall }) => { + const parsedData = useMemo(() => { + if (!decodedCall) return null; + + return { + tokenAddress: decodedCall.to, + amount: BigNumber.from(decodedCall.args._value), + source: decodedCall.from, + destination: decodedCall.args._to as string, + }; + }, [decodedCall]); + + const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); + const roundedBalance = useBigNumberToNumber( + parsedData?.amount, + tokenInfo?.decimals, + 4 + ); + const { ensName, imageUrl } = useENSAvatar( + parsedData?.destination, + MAINNET_ID + ); + + return ( + <> + + + + Mint {roundedBalance} + + + + + + + + {ensName || parsedData?.destination + ? shortenAddress(parsedData?.destination) + : ''} + + + ); +}; + +export default REPMintInfoLine; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx new file mode 100644 index 0000000000..d88a866ee7 --- /dev/null +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -0,0 +1,63 @@ +import Avatar from 'components/Guilds/Avatar'; +import { BigNumber } from 'ethers'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import { useMemo } from 'react'; +import { MAINNET_ID, shortenAddress } from 'utils'; +import { ActionViewProps } from '..'; +import { Segment } from '../common/infoLine'; +import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; + +const REPMintSummary: React.FC = ({ decodedCall }) => { + const parsedData = useMemo(() => { + if (!decodedCall) return null; + + return { + tokenAddress: decodedCall.to, + amount: BigNumber.from(decodedCall.args._value), + source: decodedCall.from, + destination: decodedCall.args._to, + }; + }, [decodedCall]); + + const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); + const roundedBalance = useBigNumberToNumber( + parsedData?.amount, + tokenInfo?.decimals, + 4 + ); + const { ensName, imageUrl } = useENSAvatar( + parsedData?.destination, + MAINNET_ID + ); + + return ( + <> + + Receiver + Amount + + + + + + + + + {ensName || shortenAddress(parsedData?.destination)} + + + + {roundedBalance} {tokenInfo?.symbol} + + + + ); +}; + +export default REPMintSummary; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx index a8c0930f37..b53d0909d5 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx @@ -2,9 +2,13 @@ import { BigNumber, utils } from 'ethers'; import { DeepPartial, RequireAtLeastOne } from 'utils/types'; import { DecodedAction, DecodedCall, SupportedAction } from '../types'; import ERC20ABI from '../../../../abis/ERC20.json'; +import ERC20SnapshotRep from '../../../../contracts/ERC20SnapshotRep.json'; import ERC20TransferEditor from './ERC20Transfer/ERC20TransferEditor'; import ERC20TransferInfoLine from './ERC20Transfer/ERC20TransferInfoLine'; import ERC20TransferSummary from './ERC20Transfer/ERC20TransferSummary'; +import REPMintEditor from './REPMint/REPMintEditor'; +import REPMintInfoLine from './REPMint/REPMintInfoLine'; +import REPMintSummary from './REPMint/REPMintSummary'; export interface SupportedActionMetadata { title: string; @@ -37,6 +41,12 @@ export const supportedActions: Record< summaryView: ERC20TransferSummary, editor: ERC20TransferEditor, }, + [SupportedAction.REP_MINT]: { + title: 'Mint Reputation', + infoLineView: REPMintInfoLine, + summaryView: REPMintSummary, + editor: REPMintEditor, + }, [SupportedAction.GENERIC_CALL]: { title: 'Generic Call', infoLineView: () =>
Generic Call
, @@ -45,6 +55,7 @@ export const supportedActions: Record< }; const ERC20Contract = new utils.Interface(ERC20ABI); +const ERC20SnapshotRepContract = new utils.Interface(ERC20SnapshotRep.abi); export const defaultValues: Record< SupportedAction, @@ -62,6 +73,19 @@ export const defaultValues: Record< }, }, }, + [SupportedAction.REP_MINT]: { + contract: ERC20Contract, + decodedCall: { + function: ERC20SnapshotRepContract.getFunction('mint'), + to: '', + value: BigNumber.from(0), + args: { + _to: '', + _value: BigNumber.from(0), + }, + }, + }, + [SupportedAction.GENERIC_CALL]: {}, }; diff --git a/src/components/Guilds/ActionsBuilder/types.ts b/src/components/Guilds/ActionsBuilder/types.ts index 84a2202f89..fa49538332 100644 --- a/src/components/Guilds/ActionsBuilder/types.ts +++ b/src/components/Guilds/ActionsBuilder/types.ts @@ -3,6 +3,7 @@ import { utils } from 'ethers'; export enum SupportedAction { ERC20_TRANSFER = 'ERC20_TRANSFER', + REP_MINT = 'REP_MINT', GENERIC_CALL = 'GENERIC_CALL', } diff --git a/src/components/Guilds/ActionsModal/ContractsList.tsx b/src/components/Guilds/ActionsModal/ContractsList.tsx index 061b30de27..c5e420ef7c 100644 --- a/src/components/Guilds/ActionsModal/ContractsList.tsx +++ b/src/components/Guilds/ActionsModal/ContractsList.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { ReactComponent as Vector } from '../../../assets/images/vector.svg'; +import { ReactComponent as Mint } from '../../../assets/images/mint.svg'; import StyledIcon from '../common/SVG'; import { RegistryContract, @@ -42,6 +43,14 @@ const ContractsList: React.FC = ({ Transfer & Mint + onSupportedActionSelect(SupportedAction.REP_MINT)} + > + + + Mint REP + + External Contracts From 473d33d53a90a0e92bec41d56a4dd65c235b4771 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Sun, 10 Apr 2022 16:20:42 +0200 Subject: [PATCH 20/67] fix: shows the users address when minting by default now --- .../REPMint/REPMintEditor.tsx | 135 ++++-------------- .../REPMint/REPMintInfoLine.tsx | 51 +++---- .../REPMint/REPMintSummary.tsx | 50 +++---- 3 files changed, 63 insertions(+), 173 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx index 6f7567c99e..f16ea54949 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -1,17 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import styled from 'styled-components'; import { Input } from 'components/Guilds/common/Form/Input'; -import { FiX } from 'react-icons/fi'; import Avatar from 'components/Guilds/Avatar'; import { useWeb3React } from '@web3-react/core'; -import { useTokenList } from 'hooks/Guilds/tokens/useTokenList'; import { useMemo } from 'react'; import { ActionEditorProps } from '..'; -import { BigNumber, utils } from 'ethers'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import { utils } from 'ethers'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; import { Box } from 'components/Guilds/common/Layout'; -import { MAINNET_ID, shortenAddress } from 'utils'; +import { shortenAddress, MAINNET_ID } from 'utils'; import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; import { baseInputStyles } from 'components/Guilds/common/Form/Input'; import { ReactComponent as Info } from '../../../../../assets/images/info.svg'; @@ -39,12 +36,6 @@ const ControlRow = styled(Box)` height: 100%; `; -const ClickableIcon = styled(Box)` - display: flex; - align-items: center; - cursor: pointer; -`; - const RepMintInput = styled(NumericalInput)` ${baseInputStyles} display: flex; @@ -56,78 +47,34 @@ const RepMintInput = styled(NumericalInput)` } `; -interface TransferState { - source: string; - tokenAddress: string; - amount: BigNumber; - destination: string; -} - -const Mint: React.FC = ({ decodedCall, updateCall }) => { - const { chainId } = useWeb3React(); +// interface REPMintState { +// source: string; +// tokenAddress: string; +// amount: BigNumber; +// destination: string; +// } +const Mint: React.FC = ({ decodedCall }) => { // parse transfer state from calls - const parsedData = useMemo(() => { - if (!decodedCall) return null; + // const parsedData = useMemo(() => { + // if (!decodedCall) return null; - return { - source: decodedCall.from, - tokenAddress: decodedCall.to, - amount: decodedCall.args._value, - destination: decodedCall.args._to, - }; - }, [decodedCall]); + // return { + // source: decodedCall.from, + // tokenAddress: decodedCall.to, + // amount: decodedCall.args._value, + // destination: decodedCall.args._to, + // }; + // }, [decodedCall]); + + const { account: userAddress } = useWeb3React(); + const { imageUrl } = useENSAvatar(userAddress, MAINNET_ID); const validations = useMemo(() => { return { - tokenAddress: utils.isAddress(parsedData?.tokenAddress), - amount: BigNumber.isBigNumber(parsedData?.amount), - destination: utils.isAddress(parsedData?.destination), + destination: utils.isAddress(userAddress), }; - }, [parsedData]); - - // // Get token details from the token address - const { tokens } = useTokenList(chainId); - const token = useMemo(() => { - if (!parsedData?.tokenAddress || !tokens) return null; - - return tokens.find(({ address }) => address === parsedData.tokenAddress); - }, [tokens, parsedData]); - console.log(token); - - const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); - const roundedBalance = useBigNumberToNumber( - parsedData?.amount, - tokenInfo?.decimals, - 10 - ); - const { ensName, imageUrl: destinationAvatarUrl } = useENSAvatar( - parsedData?.destination, - MAINNET_ID - ); - - const setTransferAddress = (walletAddress: string) => { - updateCall({ - ...decodedCall, - args: { - ...decodedCall.args, - _to: walletAddress, - }, - }); - }; - - const setAmount = (value: string) => { - const amount = value - ? utils.parseUnits(value, tokenInfo?.decimals || 18) - : null; - updateCall({ - ...decodedCall, - args: { - ...decodedCall.args, - _value: amount, - }, - }); - }; + }, [userAddress]); return (
@@ -138,33 +85,13 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { - {validations.destination && ( - <> - - - {ensName || parsedData?.destination - ? shortenAddress(parsedData?.destination) - : ''} - - - )} -
- } - iconRight={ - parsedData?.destination ? ( - setTransferAddress('')}> - - - ) : null + validations.destination && ( + + ) } - onChange={e => setTransferAddress(e.target.value)} + readOnly /> @@ -174,7 +101,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { Reputation in % - + @@ -184,7 +111,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { Reputation Amount - + diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx index 3cfb137ae8..dc7024406b 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx @@ -1,9 +1,6 @@ import Avatar from 'components/Guilds/Avatar'; -import { BigNumber } from 'ethers'; -import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import { useMemo } from 'react'; +// import { useMemo } from 'react'; import { FiArrowRight } from 'react-icons/fi'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; @@ -11,55 +8,39 @@ import { Segment } from '../common/infoLine'; import { ReactComponent as Mint } from '../../../../../assets/images/mint.svg'; import StyledIcon from 'components/Guilds/common/SVG'; import styled from 'styled-components'; +import { useWeb3React } from '@web3-react/core'; const StyledMintIcon = styled(StyledIcon)` margin: 0; `; const REPMintInfoLine: React.FC = ({ decodedCall }) => { - const parsedData = useMemo(() => { - if (!decodedCall) return null; + const { account: userAddress } = useWeb3React(); + const { ensName, imageUrl } = useENSAvatar(userAddress, MAINNET_ID); + // const parsedData = useMemo(() => { + // if (!decodedCall) return null; - return { - tokenAddress: decodedCall.to, - amount: BigNumber.from(decodedCall.args._value), - source: decodedCall.from, - destination: decodedCall.args._to as string, - }; - }, [decodedCall]); - - const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); - const roundedBalance = useBigNumberToNumber( - parsedData?.amount, - tokenInfo?.decimals, - 4 - ); - const { ensName, imageUrl } = useENSAvatar( - parsedData?.destination, - MAINNET_ID - ); + // return { + // tokenAddress: decodedCall.to, + // amount: BigNumber.from(decodedCall.args._value), + // source: decodedCall.from, + // destination: decodedCall.args._to as string, + // }; + // }, [decodedCall]); return ( <> - Mint {roundedBalance} + Mint {''} - - - - {ensName || parsedData?.destination - ? shortenAddress(parsedData?.destination) - : ''} + + {ensName || shortenAddress(userAddress)} ); }; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index d88a866ee7..f51c26cbd7 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -1,36 +1,26 @@ import Avatar from 'components/Guilds/Avatar'; -import { BigNumber } from 'ethers'; -import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import { useMemo } from 'react'; +// import { useMemo } from 'react'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; import { Segment } from '../common/infoLine'; import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; +import { useWeb3React } from '@web3-react/core'; const REPMintSummary: React.FC = ({ decodedCall }) => { - const parsedData = useMemo(() => { - if (!decodedCall) return null; + // const parsedData = useMemo(() => { + // if (!decodedCall) return null; - return { - tokenAddress: decodedCall.to, - amount: BigNumber.from(decodedCall.args._value), - source: decodedCall.from, - destination: decodedCall.args._to, - }; - }, [decodedCall]); + // return { + // tokenAddress: decodedCall.to, + // amount: BigNumber.from(decodedCall.args._value), + // source: decodedCall.from, + // destination: decodedCall.args._to, + // }; + // }, [decodedCall]); - const { data: tokenInfo } = useERC20Info(parsedData?.tokenAddress); - const roundedBalance = useBigNumberToNumber( - parsedData?.amount, - tokenInfo?.decimals, - 4 - ); - const { ensName, imageUrl } = useENSAvatar( - parsedData?.destination, - MAINNET_ID - ); + const { account: userAddress } = useWeb3React(); + const { ensName, imageUrl } = useENSAvatar(userAddress, MAINNET_ID); return ( <> @@ -42,19 +32,11 @@ const REPMintSummary: React.FC = ({ decodedCall }) => { - - - - {ensName || shortenAddress(parsedData?.destination)} + + {ensName || shortenAddress(userAddress)} - - {roundedBalance} {tokenInfo?.symbol} - + 100 REP ); From 930a5675afd3d6af6d8723549b778f7cd8db1663 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Sun, 10 Apr 2022 22:46:41 +0200 Subject: [PATCH 21/67] feat: enabled full functionality according to specs. Last part is to sync with a contract --- .../ERC20Transfer/ERC20TransferEditor.tsx | 1 - .../REPMint/REPMintEditor.tsx | 98 ++++++++++++------- .../REPMint/REPMintInfoLine.tsx | 38 ++++--- .../REPMint/REPMintSummary.tsx | 40 +++++--- .../ActionsBuilder/SupportedActions/index.tsx | 9 +- .../Guilds/ActionsModal/ContractsList.tsx | 5 +- src/components/Guilds/ActionsModal/index.tsx | 8 +- 7 files changed, 125 insertions(+), 74 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx index 2e68875941..ff7676218d 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/ERC20Transfer/ERC20TransferEditor.tsx @@ -70,7 +70,6 @@ const Transfer: React.FC = ({ decodedCall, updateCall }) => { // parse transfer state from calls const parsedData = useMemo(() => { if (!decodedCall) return null; - return { source: decodedCall.from, tokenAddress: decodedCall.to, diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx index f16ea54949..c9de1cfbce 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -2,17 +2,17 @@ import styled from 'styled-components'; import { Input } from 'components/Guilds/common/Form/Input'; import Avatar from 'components/Guilds/Avatar'; -import { useWeb3React } from '@web3-react/core'; import { useMemo } from 'react'; import { ActionEditorProps } from '..'; -import { utils } from 'ethers'; +import { BigNumber } from 'ethers'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; import { Box } from 'components/Guilds/common/Layout'; import { shortenAddress, MAINNET_ID } from 'utils'; -import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; import { baseInputStyles } from 'components/Guilds/common/Form/Input'; import { ReactComponent as Info } from '../../../../../assets/images/info.svg'; import StyledIcon from 'components/Guilds/common/SVG'; +// import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import { useState } from 'react'; const Control = styled(Box)` display: flex; @@ -36,7 +36,7 @@ const ControlRow = styled(Box)` height: 100%; `; -const RepMintInput = styled(NumericalInput)` +const RepMintInput = styled(Input)` ${baseInputStyles} display: flex; align-items: center; @@ -47,34 +47,61 @@ const RepMintInput = styled(NumericalInput)` } `; -// interface REPMintState { -// source: string; -// tokenAddress: string; -// amount: BigNumber; -// destination: string; -// } +interface REPMintState { + toAddress: string; + amount: BigNumber; +} -const Mint: React.FC = ({ decodedCall }) => { +const Mint: React.FC = ({ decodedCall, updateCall }) => { // parse transfer state from calls - // const parsedData = useMemo(() => { - // if (!decodedCall) return null; + console.log({ decodedCall }); + const [repPercent, setRepPercent] = useState(0); + const [repAmount, setRepAmount] = useState(0); + const parsedData = useMemo(() => { + if (!decodedCall) return null; + return { + toAddress: decodedCall.args.to, + amount: decodedCall.args.amount, + }; + }, [decodedCall]); - // return { - // source: decodedCall.from, - // tokenAddress: decodedCall.to, - // amount: decodedCall.args._value, - // destination: decodedCall.args._to, - // }; - // }, [decodedCall]); + console.log({ parsedData }); + const { imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - const { account: userAddress } = useWeb3React(); - const { imageUrl } = useENSAvatar(userAddress, MAINNET_ID); + const setCallDataAmount = (value: string) => { + const amount = value ? BigNumber.from(value) : null; + updateCall({ + ...decodedCall, + args: { + ...decodedCall.args, + amount, + }, + }); + }; - const validations = useMemo(() => { - return { - destination: utils.isAddress(userAddress), - }; - }, [userAddress]); + const handleRepPercentChange = (e: React.ChangeEvent) => { + if (e.target.value) { + setRepPercent(parseInt(e.target.value)); + setRepAmount(parseInt(e.target.value) * 100); + setCallDataAmount((parseInt(e.target.value) * 100).toString()); + } else { + setRepPercent(0); + setRepAmount(0); + setCallDataAmount('0'); + } + }; + + const handleRepAmountChange = (e: React.ChangeEvent) => { + if (e.target.value) { + setRepAmount(parseInt(e.target.value)); + setRepPercent(parseInt(e.target.value) / 100); + setCallDataAmount(e.target.value); + } else { + setRepPercent(0); + setRepAmount(0); + setCallDataAmount('0'); + } + }; return (
@@ -85,11 +112,13 @@ const Mint: React.FC = ({ decodedCall }) => { - ) + } readOnly /> @@ -101,7 +130,10 @@ const Mint: React.FC = ({ decodedCall }) => { Reputation in % - + @@ -111,7 +143,7 @@ const Mint: React.FC = ({ decodedCall }) => { Reputation Amount - + diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx index dc7024406b..c6a3f52673 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx @@ -1,6 +1,6 @@ import Avatar from 'components/Guilds/Avatar'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -// import { useMemo } from 'react'; +import { useMemo } from 'react'; import { FiArrowRight } from 'react-icons/fi'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; @@ -8,39 +8,45 @@ import { Segment } from '../common/infoLine'; import { ReactComponent as Mint } from '../../../../../assets/images/mint.svg'; import StyledIcon from 'components/Guilds/common/SVG'; import styled from 'styled-components'; -import { useWeb3React } from '@web3-react/core'; +import { BigNumber } from 'ethers'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; const StyledMintIcon = styled(StyledIcon)` margin: 0; `; +interface REPMintState { + guildAddress: string; + toAddress: string; + amount: BigNumber; +} + const REPMintInfoLine: React.FC = ({ decodedCall }) => { - const { account: userAddress } = useWeb3React(); - const { ensName, imageUrl } = useENSAvatar(userAddress, MAINNET_ID); - // const parsedData = useMemo(() => { - // if (!decodedCall) return null; + const parsedData = useMemo(() => { + if (!decodedCall) return null; + return { + guildAddress: decodedCall.to, + toAddress: decodedCall.args.to, + amount: decodedCall.args.amount, + }; + }, [decodedCall]); + const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - // return { - // tokenAddress: decodedCall.to, - // amount: BigNumber.from(decodedCall.args._value), - // source: decodedCall.from, - // destination: decodedCall.args._to as string, - // }; - // }, [decodedCall]); + const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 1, 2) * 10; return ( <> - Mint {''} + Mint {roundedRepAmount} % - + - {ensName || shortenAddress(userAddress)} + {ensName || shortenAddress(parsedData?.toAddress)} ); }; diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index f51c26cbd7..e3f72df724 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -1,26 +1,30 @@ import Avatar from 'components/Guilds/Avatar'; +import { BigNumber } from 'ethers'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -// import { useMemo } from 'react'; +import { useMemo } from 'react'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; import { Segment } from '../common/infoLine'; import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; -import { useWeb3React } from '@web3-react/core'; + +interface REPMintState { + toAddress: string; + amount: BigNumber; +} const REPMintSummary: React.FC = ({ decodedCall }) => { - // const parsedData = useMemo(() => { - // if (!decodedCall) return null; + const parsedData = useMemo(() => { + if (!decodedCall) return null; + return { + toAddress: decodedCall.args.to, + amount: decodedCall.args.amount, + }; + }, [decodedCall]); - // return { - // tokenAddress: decodedCall.to, - // amount: BigNumber.from(decodedCall.args._value), - // source: decodedCall.from, - // destination: decodedCall.args._to, - // }; - // }, [decodedCall]); + const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - const { account: userAddress } = useWeb3React(); - const { ensName, imageUrl } = useENSAvatar(userAddress, MAINNET_ID); + const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 1, 3) * 10; return ( <> @@ -32,11 +36,15 @@ const REPMintSummary: React.FC = ({ decodedCall }) => { - + - {ensName || shortenAddress(userAddress)} + {ensName || shortenAddress(parsedData?.toAddress)} - 100 REP + {roundedRepAmount} REP ); diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx index b53d0909d5..d49fda4769 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/index.tsx @@ -9,7 +9,6 @@ import ERC20TransferSummary from './ERC20Transfer/ERC20TransferSummary'; import REPMintEditor from './REPMint/REPMintEditor'; import REPMintInfoLine from './REPMint/REPMintInfoLine'; import REPMintSummary from './REPMint/REPMintSummary'; - export interface SupportedActionMetadata { title: string; } @@ -53,10 +52,8 @@ export const supportedActions: Record< editor: () =>
Generic Call Editor
, }, }; - const ERC20Contract = new utils.Interface(ERC20ABI); const ERC20SnapshotRepContract = new utils.Interface(ERC20SnapshotRep.abi); - export const defaultValues: Record< SupportedAction, DeepPartial @@ -74,14 +71,14 @@ export const defaultValues: Record< }, }, [SupportedAction.REP_MINT]: { - contract: ERC20Contract, + contract: ERC20SnapshotRepContract, decodedCall: { function: ERC20SnapshotRepContract.getFunction('mint'), to: '', value: BigNumber.from(0), args: { - _to: '', - _value: BigNumber.from(0), + to: '', + amount: BigNumber.from(0), }, }, }, diff --git a/src/components/Guilds/ActionsModal/ContractsList.tsx b/src/components/Guilds/ActionsModal/ContractsList.tsx index c5e420ef7c..8c18661624 100644 --- a/src/components/Guilds/ActionsModal/ContractsList.tsx +++ b/src/components/Guilds/ActionsModal/ContractsList.tsx @@ -55,7 +55,10 @@ const ContractsList: React.FC = ({ External Contracts {contracts?.map(contract => ( - onSelect(contract)}> + onSelect(contract)} + > {contract.title} {contract.functions?.length}{' '} diff --git a/src/components/Guilds/ActionsModal/index.tsx b/src/components/Guilds/ActionsModal/index.tsx index faf9e53437..e207f30997 100644 --- a/src/components/Guilds/ActionsModal/index.tsx +++ b/src/components/Guilds/ActionsModal/index.tsx @@ -18,6 +18,7 @@ import { Modal } from '../common/Modal'; import ContractActionsList from './ContractActionsList'; import ContractsList from './ContractsList'; import ParamsModal from './ParamsModal'; +import { useWeb3React } from '@web3-react/core'; export const EditorWrapper = styled.div` margin: 1.25rem; @@ -40,7 +41,7 @@ const ActionModal: React.FC = ({ onAddAction, }) => { const { guild_id: guildId } = useParams<{ guild_id?: string }>(); - + const { account: walletAddress } = useWeb3React(); // Supported Actions const [selectedAction, setSelectedAction] = useState(null); const [selectedActionContract, setSelectedActionContract] = @@ -128,6 +129,11 @@ const ActionModal: React.FC = ({ defaultDecodedAction.decodedCall.from = guildId; defaultDecodedAction.decodedCall.callType = action; + switch (action) { + case SupportedAction.REP_MINT: + defaultDecodedAction.decodedCall.args.to = walletAddress; + break; + } setData(defaultDecodedAction.decodedCall); setSelectedAction(action); setSelectedActionContract(defaultDecodedAction.contract); From fbf4e98d312be945bf0385cd21ae28f79e0e24d1 Mon Sep 17 00:00:00 2001 From: Milton Tulli Date: Mon, 11 Apr 2022 09:59:31 -0300 Subject: [PATCH 22/67] Use env accounts instead of hardcoded ones (#780) --- scripts/dev.ts | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/scripts/dev.ts b/scripts/dev.ts index 112b4cb9bb..0332ff6858 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -9,34 +9,12 @@ import { const hre = require('hardhat'); const moment = require('moment'); -const accounts = [ - '0x79706c8e413cdaee9e63f282507287b9ea9c0928', - '0xc73480525e9d1198d448ece4a01daea851f72a9d', - '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', - '0xaf1a6415202453d79b84d8a9d055b8f9141f696b', - '0x02803e2cdff171d1910d178dac948c711937bd3f', - '0x797c62953ef866a1ece855c4033cc5dc3c11290b', - '0x016f70459e4ba98e0d60a5f5855e117e8ff39cae', - '0x073f4fdc12f805b8d98e58551fc10d0a71bbc7db', - '0x6829556f30899d70403947590ffe8900e8c0d0d7', - '0x2b410bcb3b8096164fa8c6a06220a66bfb77058d', - '0x309f75c54a57937a7a0c6eb9b36eb1dbca82407e', - '0xec9d2d34ad6acda19ab8afe71595051b206e3e4d', - '0x40c23c536bad1fe206ce911114f2c70309a7e487', - '0x28d254f2ddb522c43a21d338e337fd8d2f820db2', - '0xaf7386ce842cc0cffef91361059b0ca9ae48d6a0', - '0x46c18451aaead6a2cb888b5bd6193c0f2c402329', - '0xc707c8143a6e1274ae7f637946f685870925261f', - '0x5b14a88dbbb04abcb6e5bf6384491be8d939cf57', - '0x92d356240dda25d050aa441690b92b2fa0011b84', - '0x5a485c203d9537095a6be2acc5a7ad83805d301d', -]; - async function main() { const web3 = hre.web3; const PermissionRegistry = await hre.artifacts.require('PermissionRegistry'); const ERC20Guild = await hre.artifacts.require('ERC20Guild'); const SnapshotERC20Guild = await hre.artifacts.require('SnapshotERC20Guild'); + const accounts = await web3.eth.getAccounts(); const deployconfig = { reputation: [ From e5d76801e35ff0a6a23dbae14eefdacd0a5fd3d2 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Wed, 13 Apr 2022 14:48:45 +0200 Subject: [PATCH 23/67] feat: mint function now reads from the call --- .../REPMint/REPMintEditor.tsx | 56 ++++++++----------- .../REPMint/REPMintInfoLine.tsx | 14 ++++- .../REPMint/REPMintSummary.tsx | 2 +- .../Guilds/ether-swr/erc20/useERC20Info.ts | 4 ++ 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx index c9de1cfbce..b1d46d509f 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -2,17 +2,20 @@ import styled from 'styled-components'; import { Input } from 'components/Guilds/common/Form/Input'; import Avatar from 'components/Guilds/Avatar'; -import { useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import { ActionEditorProps } from '..'; -import { BigNumber } from 'ethers'; +import { BigNumber, ethers } from 'ethers'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; import { Box } from 'components/Guilds/common/Layout'; import { shortenAddress, MAINNET_ID } from 'utils'; import { baseInputStyles } from 'components/Guilds/common/Form/Input'; import { ReactComponent as Info } from '../../../../../assets/images/info.svg'; import StyledIcon from 'components/Guilds/common/SVG'; -// import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import { useState } from 'react'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; +import { useParams } from 'react-router-dom'; const Control = styled(Box)` display: flex; @@ -54,9 +57,14 @@ interface REPMintState { const Mint: React.FC = ({ decodedCall, updateCall }) => { // parse transfer state from calls - console.log({ decodedCall }); - const [repPercent, setRepPercent] = useState(0); - const [repAmount, setRepAmount] = useState(0); + const [repPercent, setRepPercent] = useState(null); + const [repAmount, setRepAmount] = useState(null); + const { guild_id: guildId } = + useParams<{ chain_name?: string; guild_id?: string }>(); + const { data } = useGuildConfig(guildId); + const { data: tokenData } = useERC20Info(data?.token); + const totalSupply = useBigNumberToNumber(tokenData?.totalSupply, 18); + const parsedData = useMemo(() => { if (!decodedCall) return null; return { @@ -65,11 +73,10 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { }; }, [decodedCall]); - console.log({ parsedData }); const { imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); const setCallDataAmount = (value: string) => { - const amount = value ? BigNumber.from(value) : null; + const amount = value ? ethers.utils.parseUnits(value) : null; updateCall({ ...decodedCall, args: { @@ -79,30 +86,18 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { }); }; - const handleRepPercentChange = (e: React.ChangeEvent) => { - if (e.target.value) { - setRepPercent(parseInt(e.target.value)); - setRepAmount(parseInt(e.target.value) * 100); - setCallDataAmount((parseInt(e.target.value) * 100).toString()); - } else { - setRepPercent(0); - setRepAmount(0); - setCallDataAmount('0'); + useEffect(() => { + setRepAmount((repPercent / 100) * totalSupply); + if (repAmount) { + setCallDataAmount(repAmount.toString()); } - }; + }, [repPercent, repAmount, totalSupply]); - const handleRepAmountChange = (e: React.ChangeEvent) => { + const handleRepChange = (e: React.ChangeEvent) => { if (e.target.value) { - setRepAmount(parseInt(e.target.value)); - setRepPercent(parseInt(e.target.value) / 100); - setCallDataAmount(e.target.value); - } else { - setRepPercent(0); - setRepAmount(0); - setCallDataAmount('0'); + setRepPercent(parseFloat(e.target.value)); } }; - return (
@@ -130,10 +125,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { Reputation in % - + @@ -143,7 +135,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { Reputation Amount - + diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx index c6a3f52673..831dc515c6 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx @@ -10,6 +10,9 @@ import StyledIcon from 'components/Guilds/common/SVG'; import styled from 'styled-components'; import { BigNumber } from 'ethers'; import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; +import { useParams } from 'react-router-dom'; const StyledMintIcon = styled(StyledIcon)` margin: 0; @@ -22,6 +25,12 @@ interface REPMintState { } const REPMintInfoLine: React.FC = ({ decodedCall }) => { + const { guild_id: guildId } = + useParams<{ chain_name?: string; guild_id?: string }>(); + const { data } = useGuildConfig(guildId); + const { data: tokenData } = useERC20Info(data?.token); + const totalSupply = useBigNumberToNumber(tokenData?.totalSupply, 18); + const parsedData = useMemo(() => { if (!decodedCall) return null; return { @@ -32,14 +41,15 @@ const REPMintInfoLine: React.FC = ({ decodedCall }) => { }, [decodedCall]); const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 1, 2) * 10; + const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 16); + const roundedRepPercent = roundedRepAmount / totalSupply; return ( <> - Mint {roundedRepAmount} % + Mint {roundedRepPercent} % diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index e3f72df724..9235bd1d38 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -24,7 +24,7 @@ const REPMintSummary: React.FC = ({ decodedCall }) => { const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 1, 3) * 10; + const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 18); return ( <> diff --git a/src/hooks/Guilds/ether-swr/erc20/useERC20Info.ts b/src/hooks/Guilds/ether-swr/erc20/useERC20Info.ts index d5041fa784..428d5a50a9 100644 --- a/src/hooks/Guilds/ether-swr/erc20/useERC20Info.ts +++ b/src/hooks/Guilds/ether-swr/erc20/useERC20Info.ts @@ -1,11 +1,13 @@ import useEtherSWR from '../useEtherSWR'; import ERC20ABI from '../../../../abis/ERC20.json'; import { useMemo } from 'react'; +import { BigNumber } from 'ethers'; export type ERC20Info = { name: string; symbol: string; decimals: number; + totalSupply: BigNumber; }; export const useERC20Info = (contractAddress: string) => { @@ -15,6 +17,7 @@ export const useERC20Info = (contractAddress: string) => { [contractAddress, 'name'], [contractAddress, 'symbol'], [contractAddress, 'decimals'], + [contractAddress, 'totalSupply'], ] : [], { @@ -29,6 +32,7 @@ export const useERC20Info = (contractAddress: string) => { name: data[0], symbol: data[1], decimals: data[2], + totalSupply: data[3], }; }, [data]); From 3959fda79d94aa570f790e1d45ec51b0c0008667 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 13 Apr 2022 12:12:02 -0300 Subject: [PATCH 24/67] fix(cacheservice): get execution events from Multicall schemes --- src/services/CacheService.ts | 118 ++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 21 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 32665493b6..34453f7b09 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1281,23 +1281,56 @@ export default class UtilsService { ); } else { if (schemeTypeData.type === 'GenericMulticall') { + // event ProposalExecutedByVotingMachine( + // address indexed _avatar, + // bytes32 indexed _proposalId, + // int256 _param + // ); + const votingMachineExecutionEvent = + schemeProposalInfo.state === + WalletSchemeProposalState.Submitted + ? await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + [ + '0x25d4c89430c1f10c60c292556941e3e624ec1ec04972a5da46cee1b352429cbe', + avatarAddressEncoded, + proposalId, + ], + 10000000 + ) + : []; + + if ( + votingMachineExecutionEvent.length > 0 && + votingMachineExecutionEvent[0].data === + '0x0000000000000000000000000000000000000000000000000000000000000001' + ) + schemeProposalInfo.state = + WalletSchemeProposalState.Submitted; + else if (votingMachineExecutionEvent.length > 0) { + schemeProposalInfo.state = + WalletSchemeProposalState.Rejected; + } + const executionEvent = await getRawEvents( web3, schemeAddress, - schemeEvent.blockNumber, + fromBlock, toBlock, [ '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', avatarAddressEncoded, proposalId, - ] + ], + 10000000 ); - if (executionEvent.length > 0) + if (executionEvent.length > 0) { schemeProposalInfo.state = WalletSchemeProposalState.ExecutionSucceded; - else - schemeProposalInfo.state = - WalletSchemeProposalState.Submitted; + } } else if (schemeTypeData.type === 'ContributionReward') { if ( callsResponse.decodedReturnData[5] > 0 || @@ -1891,23 +1924,66 @@ export default class UtilsService { } } } else if (schemeTypeData.type === 'GenericMulticall') { - const executionEvent = await await getRawEvents( - web3, - schemeAddress, - networkCache.proposals[proposal.id].creationEvent.blockNumber, - toBlock, - [ - '0x6bc0cb9e9967b59a69ace442598e1df4368d38661bd5c0800fbcbc9fe855fbbe', - avatarAddressEncoded, - proposal.id, - ] - ); - if (executionEvent.length > 0) + // event ProposalExecutedByVotingMachine( + // address indexed _avatar, + // bytes32 indexed _proposalId, + // int256 _param + // ); + const votingMachineExecutionEvent = + networkCache.proposals[proposal.id].stateInScheme === + WalletSchemeProposalState.Submitted + ? await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + [ + '0x25d4c89430c1f10c60c292556941e3e624ec1ec04972a5da46cee1b352429cbe', + avatarAddressEncoded, + proposal.id, + ], + 10000000 + ) + : []; + + if ( + votingMachineExecutionEvent.length > 0 && + votingMachineExecutionEvent[0].data === + '0x0000000000000000000000000000000000000000000000000000000000000001' + ) + networkCache.proposals[proposal.id].stateInVotingMachine = + VotingMachineProposalState.Executed; + else if (votingMachineExecutionEvent.length > 0) { networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.ExecutionSucceded; - else + WalletSchemeProposalState.Rejected; + networkCache.proposals[proposal.id].stateInVotingMachine = + VotingMachineProposalState.Rejected; + } + + // event ProposalExecuted( + // address indexed _avatar, + // bytes32 indexed _proposalId + // ); + const executionEvent = + networkCache.proposals[proposal.id].stateInVotingMachine === + VotingMachineProposalState.Passed + ? await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + [ + '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', + avatarAddressEncoded, + proposal.id, + ], + 10000000 + ) + : []; + if (executionEvent.length > 0) { networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.Submitted; + WalletSchemeProposalState.ExecutionSucceded; + } } else if ( networkCache.proposals[proposal.id].stateInVotingMachine === VotingMachineProposalState.Executed From ae87551c1bf120885371a1e00b27eaa321912f26 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 13 Apr 2022 12:15:12 -0300 Subject: [PATCH 25/67] fix(utils/cache): not change maxBlocksPerFetch too much --- src/utils/cache.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 162f2501da..52d7a12f90 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -33,6 +33,7 @@ export const getEvents = async function ( } catch (error) { if ((error as Error).message.indexOf('Relay attempts exhausted') > -1) { const blocksToLower = Math.max(Math.trunc((to - from) / 2), 10000); + console.error('Relay attempts exhausted'); console.debug('Lowering toBlock', blocksToLower, 'blocks'); to = to - blocksToLower; await sleep(5000); @@ -41,8 +42,11 @@ export const getEvents = async function ( 'You cannot query logs for more than 100000 blocks at once.' ) > -1 ) { - maxBlocksPerFetch = 100000; + maxBlocksPerFetch = maxBlocksPerFetch / 4; to = from + 100000; + console.error( + 'You cannot query logs for more than 100000 blocks at once.' + ); console.debug('Lowering toBlock to', to); } else if ( (error as Error).message.indexOf('Relay attempts exhausted') === -1 && @@ -87,15 +91,23 @@ export const getRawEvents = async function ( from = to; to = Math.min(from + maxBlocksPerFetch, toBlock); } catch (error) { - if ( - (error as Error).message.indexOf( - 'You cannot query logs for more than 100000 blocks at once.' - ) > -1 - ) { + if ((error as Error).message.indexOf('Relay attempts exhausted') > -1) { const blocksToLower = Math.max(Math.trunc((to - from) / 2), 10000); + console.error('Relay attempts exhausted'); console.debug('Lowering toBlock', blocksToLower, 'blocks'); to = to - blocksToLower; await sleep(5000); + } else if ( + (error as Error).message.indexOf( + 'You cannot query logs for more than 100000 blocks at once.' + ) > -1 + ) { + maxBlocksPerFetch = maxBlocksPerFetch / 4; + to = from + 100000; + console.error( + 'You cannot query logs for more than 100000 blocks at once.' + ); + console.debug('Lowering toBlock to', to); } else if ( (error as Error).message.indexOf('Relay attempts exhausted') === -1 && Math.trunc((to - from) / 2) > 10000 From c0d182cd0fc51eea923ed428538058b8b77e5c5e Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 13 Apr 2022 12:51:27 -0300 Subject: [PATCH 26/67] fix(utils/cache): dont use decimals in maxBlockPerFetch --- src/utils/cache.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 52d7a12f90..9c79ef731d 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -42,7 +42,7 @@ export const getEvents = async function ( 'You cannot query logs for more than 100000 blocks at once.' ) > -1 ) { - maxBlocksPerFetch = maxBlocksPerFetch / 4; + maxBlocksPerFetch = Number((maxBlocksPerFetch / 4).toFixed(0)); to = from + 100000; console.error( 'You cannot query logs for more than 100000 blocks at once.' @@ -102,7 +102,7 @@ export const getRawEvents = async function ( 'You cannot query logs for more than 100000 blocks at once.' ) > -1 ) { - maxBlocksPerFetch = maxBlocksPerFetch / 4; + maxBlocksPerFetch = Number((maxBlocksPerFetch / 4).toFixed(0)); to = from + 100000; console.error( 'You cannot query logs for more than 100000 blocks at once.' From e2bed435fe965746748803f9cb971cce2ef63000 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Wed, 13 Apr 2022 23:08:11 +0200 Subject: [PATCH 27/67] feat: added REPGuild check, dynamic token name, and Numerical input --- .../REPMint/REPMintEditor.tsx | 22 ++++++++++------- .../REPMint/REPMintInfoLine.tsx | 2 +- .../REPMint/REPMintSummary.tsx | 15 ++++++++++-- .../Guilds/ActionsModal/ContractsList.tsx | 24 ++++++++++++------- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx index b1d46d509f..179193d7b5 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import styled from 'styled-components'; import { Input } from 'components/Guilds/common/Form/Input'; import Avatar from 'components/Guilds/Avatar'; @@ -16,6 +15,7 @@ import { useState } from 'react'; import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; import { useParams } from 'react-router-dom'; +import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; const Control = styled(Box)` display: flex; @@ -39,7 +39,7 @@ const ControlRow = styled(Box)` height: 100%; `; -const RepMintInput = styled(Input)` +const RepMintInput = styled(NumericalInput)` ${baseInputStyles} display: flex; align-items: center; @@ -57,8 +57,8 @@ interface REPMintState { const Mint: React.FC = ({ decodedCall, updateCall }) => { // parse transfer state from calls - const [repPercent, setRepPercent] = useState(null); - const [repAmount, setRepAmount] = useState(null); + const [repPercent, setRepPercent] = useState(0); + const [repAmount, setRepAmount] = useState(0); const { guild_id: guildId } = useParams<{ chain_name?: string; guild_id?: string }>(); const { data } = useGuildConfig(guildId); @@ -93,9 +93,9 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { } }, [repPercent, repAmount, totalSupply]); - const handleRepChange = (e: React.ChangeEvent) => { - if (e.target.value) { - setRepPercent(parseFloat(e.target.value)); + const handleRepChange = (e: number) => { + if (e) { + setRepPercent(e); } }; return ( @@ -125,7 +125,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { Reputation in % - + @@ -135,7 +135,11 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { Reputation Amount - + diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx index 831dc515c6..1ead7cc7c3 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx @@ -41,7 +41,7 @@ const REPMintInfoLine: React.FC = ({ decodedCall }) => { }, [decodedCall]); const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 16); + const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 16, 3); const roundedRepPercent = roundedRepAmount / totalSupply; return ( diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index 9235bd1d38..4698c7cbd2 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -2,7 +2,10 @@ import Avatar from 'components/Guilds/Avatar'; import { BigNumber } from 'ethers'; import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; +import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; +import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; import { useMemo } from 'react'; +import { useParams } from 'react-router-dom'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; import { Segment } from '../common/infoLine'; @@ -14,6 +17,12 @@ interface REPMintState { } const REPMintSummary: React.FC = ({ decodedCall }) => { + const { guild_id: guildId } = + useParams<{ chain_name?: string; guild_id?: string }>(); + const { data } = useGuildConfig(guildId); + const { data: tokenData } = useERC20Info(data?.token); + console.log({ tokenData }); + console.log(tokenData.symbol); const parsedData = useMemo(() => { if (!decodedCall) return null; return { @@ -24,7 +33,7 @@ const REPMintSummary: React.FC = ({ decodedCall }) => { const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); - const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 18); + const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 18, 3); return ( <> @@ -44,7 +53,9 @@ const REPMintSummary: React.FC = ({ decodedCall }) => { {ensName || shortenAddress(parsedData?.toAddress)} - {roundedRepAmount} REP + + {roundedRepAmount} {tokenData?.name} + ); diff --git a/src/components/Guilds/ActionsModal/ContractsList.tsx b/src/components/Guilds/ActionsModal/ContractsList.tsx index 8c18661624..178cc052fd 100644 --- a/src/components/Guilds/ActionsModal/ContractsList.tsx +++ b/src/components/Guilds/ActionsModal/ContractsList.tsx @@ -16,6 +16,8 @@ import { SectionWrapper, Wrapper, } from './styles'; +import useGuildImplementationTypeConfig from 'hooks/Guilds/guild/useGuildImplementationType'; +import { useParams } from 'react-router-dom'; interface ContractsListProps { onSelect: (contract: RegistryContract) => void; @@ -28,7 +30,9 @@ const ContractsList: React.FC = ({ }) => { const { chainId } = useWeb3React(); const { contracts } = useContractRegistry(chainId); - + const { guild_id: guildAddress } = + useParams<{ chain_name?: string; guild_id?: string }>(); + const { isRepGuild } = useGuildImplementationTypeConfig(guildAddress); return ( @@ -43,14 +47,16 @@ const ContractsList: React.FC = ({ Transfer & Mint - onSupportedActionSelect(SupportedAction.REP_MINT)} - > - - - Mint REP - - + {isRepGuild ? ( + onSupportedActionSelect(SupportedAction.REP_MINT)} + > + + + Mint REP + + + ) : null} External Contracts From 82e9d8d863c87659b702de009aaa4ae6a8f5b09f Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 18 Apr 2022 11:01:34 -0300 Subject: [PATCH 28/67] chore(cache): update dxvote cache in source to latest blocks --- src/configs/arbitrum/config.json | 4 +- src/configs/arbitrumTestnet/config.json | 4 +- src/configs/default.json | 10 +-- src/configs/mainnet/config.json | 4 +- src/configs/proposalTitles.json | 82 ++++++++++++++++++++++++- src/configs/rinkeby/config.json | 8 +-- src/configs/xdai/config.json | 4 +- 7 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/configs/arbitrum/config.json b/src/configs/arbitrum/config.json index 6676fcf5e6..9598cbc5c2 100644 --- a/src/configs/arbitrum/config.json +++ b/src/configs/arbitrum/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 338923, - "ipfsHash": "QmbRTtmYg9GWD6stfcL618bxTbuZoFa3tyySUfaTzRjA1v", - "toBlock": "6831600" + "ipfsHash": "QmRvp9A9FUVzoYdyDd7mbvz3x6oJSwRqPDcH7JFiNKFmRN", + "toBlock": "10026660" }, "contracts": { "avatar": "0x2B240b523f69b9aF3adb1C5924F6dB849683A394", diff --git a/src/configs/arbitrumTestnet/config.json b/src/configs/arbitrumTestnet/config.json index 465065a2e3..bc74ad5134 100644 --- a/src/configs/arbitrumTestnet/config.json +++ b/src/configs/arbitrumTestnet/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 3995726, - "ipfsHash": "QmRiUXQ1yGhsDVp5QrePqNxe7WFFqzuZzGrAYEgT3RFAvY", - "toBlock": "10225604" + "ipfsHash": "QmXavj3tchxsqpXCPB4g18NikoyGd2c22syDF4Sijn3f8n", + "toBlock": "11296050" }, "contracts": { "avatar": "0x6370B3Ba7D9bAd94b35dC9073540E562bDe5Fdc3", diff --git a/src/configs/default.json b/src/configs/default.json index eca4d0c8f4..7c767589ce 100644 --- a/src/configs/default.json +++ b/src/configs/default.json @@ -1,7 +1,7 @@ { - "mainnet": "QmPcDmifixW4Cdxk9UjVejnzgzBP9urdHG7nrN83FVE5Yz", - "xdai": "QmdRR3roqvAW4iHbDCvoJQSCsqSobC8ngKAdtzsCAoDEh6", - "arbitrum": "QmUoRz433SzvwBnWta86SjL2CPrT7tm1efFTVdmCnm5hQp", - "rinkeby": "QmXwwMNZQEcabFCKmEfnyZNhBx9zCoNnD1Rkq5duvyMRku", - "arbitrumTestnet": "QmNcir6bUdevg587ceYWKtQdj5P2q9ASnCRG7sFbyQkpXi" + "mainnet": "QmXW4x43LumaPZao1WrdZcGcCsR43Pfmqui9dxywr8QAxc", + "xdai": "QmW4AaiGvx19damvBVK7jn9WrCia7QV9RMdDpB2hW6JLrk", + "arbitrum": "QmQTAtR3nteZpGAHjArUk9Zu9F3UnXVmWgccXrVzzE6PGB", + "rinkeby": "QmSq3xW8cL9yPzqVJyJLKsiRaxmAKNnQ5H4zXCZDxLynZG", + "arbitrumTestnet": "QmUuwGiETkoytzoABzBS37LHqBPqMYTDEFHVkio8VrQepm" } \ No newline at end of file diff --git a/src/configs/mainnet/config.json b/src/configs/mainnet/config.json index 62b624c7ca..f7c01e76a3 100644 --- a/src/configs/mainnet/config.json +++ b/src/configs/mainnet/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 7219912, - "ipfsHash": "QmcFgHexZP5MygnFhJwHi5W7UFrSxGK5DSGNCTjmJM54NM", - "toBlock": "14270378" + "ipfsHash": "QmUVcBbXqyWkNJorCTxpsaB6L12GhPSF9TwucyDrAgnsdC", + "toBlock": "14609184" }, "contracts": { "avatar": "0x519b70055af55a007110b4ff99b0ea33071c720a", diff --git a/src/configs/proposalTitles.json b/src/configs/proposalTitles.json index 20fce928d0..bf4e0bccc2 100644 --- a/src/configs/proposalTitles.json +++ b/src/configs/proposalTitles.json @@ -1521,5 +1521,85 @@ "0xfa759273534d116b354a0a4d801d1de0b2dd0b8e773f52bd33a159f90cd2488d": "First Month Developer Proposal Levotiate", "0xfb1fada10c1640cf81e8e0260370d4824b7edae31eab12be7ca6a93348bb2b61": "REP Boost - Discord: Kobello", "0xff6c045d9ab8f514b7dce6317ac1ff16786cad69bd4ae4f75985ff0f8828b678": "xDXD Buyback Order #130 for 11.42 WETH", - "0xffc0f4a64c8ec043238ca787e58177c2e798360c29893b7190f633116f6b4aab": "GP Relayer Trade: 300 xDAI for STAKE" + "0xffc0f4a64c8ec043238ca787e58177c2e798360c29893b7190f633116f6b4aab": "GP Relayer Trade: 300 xDAI for STAKE", + "0x168ca80db89e979aa8a54c64894b6ee8a86224e5d60a9526b36f022d88f88861": "1999 DXD Withdrawal from GPv1 Relayer", + "0x445db3d3973e01f6fca2018ffed0a982c91558d92c3fd02846c0adbdf55f87dc": "Decentral Labs Worker Proposal - 18th March to 18th May", + "0x6facdbca526b5eb3c4265c9d8e6bdadc5e7b5aeb8e5dd3d6d25fa3b2db1787d0": "Funding Gas Governance Refund Contract", + "0x818f357112024d61671175e650473b2dcfd5548f4a0f0975a1ccb71324d950bf": "1999 DXD Withdrawal Request from GPv1 Relayer", + "0xa3aab310917683f96a9546f0d87b1ecdf68550f04bb068c8ff66fb36d1b7aab7": "Update swapr.eth content hash to Swapr v1.0.0 Beta 11.1", + "0xadb3c9bfa7dc97d4ee93bb155c72ca27b35ac8d2d759074634e99fa3e2a746fb": "Sponsor The Global Governance Gathering (“GGG”) event by The DAOist in Amsterdam", + "0xae733d1e248b208b9f3bb1b9ba24b7b678dc6f9594afb018574e928787b6d7cc": "Decentral Labs Worker Proposal - 18th March to 18th May", + "0xddad4a2733571ab684c746477455983cdc0b336a6c8c54997e7500d3f25baa5e": "dlabs ETH Refund - MME", + "0xe328cc853e2b3040e827e933f708766de59e0edf304573197dcca49c377087c5": "(REDO) Sponsor The Global Governance Gathering (“GGG”) event by The DAOist in Amsterdam", + "0x073eb320293e2c3ef11eee02e6f2579e16265edc8f08e7d7e250107ab09e0689": "DAI payout Zett Period February 2022", + "0x093b73e99bb222a0c80e5b65c58ffe46b9d11fbe9a38f75b5a28ba8b2d7b4972": "LINK / GNO Epoch 12 - 13 SWPR farming campaign", + "0x122f3766bca165927f6696f2bed943198ca8a0f2f2ef420d22332319ab4027fd": "HAUS / WETH Epoch 12 - 13 SWPR farming campaign", + "0x1777d3d347124bc6d7f8337ea4066648bbc0173c57d3ae9c0e86e8f83f55924c": "Decentral Labs Worker Proposal - 18th of January to 18th of March (2nd part)", + "0x184630d11dad40bbe01e7714384ff1dff5ef736f451fb5381edef2803a1a1cf1": "WETH / XDAI Epoch 12 - 13 SWPR farming campaign", + "0x229079e05bdc9905fa1d8b19c33f7e4636ee31de809fe2b039284eabea8238a4": "MAI / XDAI Epoch 14 - 15 SWPR farming campaign", + "0x2a962b078d0452d70264772a05ff39b28163ae80c3b02a27b1e63bafba035c11": "0xKLOM.eth ETHDenver Stipend Update", + "0x2c8867dec2141c3d29060ba784d88296c67e545098e1eb7363035145579a1a8a": "Deposit 415 WETH & 1.1M xDai in Swapr Liquidity Relayer", + "0x316199049ec001cb1ed0afd4a7cdc4ee1921e57ba7994c7b0b8b520ccd7fc6bd": "Mirror Proposal of \"Sponsor The Global Governance Gathering (“GGG”) event by The DAOist in Amsterdam\" on Mainnet", + "0x39aa9535792dbd6d5365cf683a848c00d9c9b332b4286e0e4c53626f5844d613": "Dlabs Entity Formation and Maintenance Contributor Stipend", + "0x3b3c72675bb5459313a9f38e0408f0cd01e7706b83634ac9a95053904a4740dc": "RICE / XDAI Epoch 14 - 15 SWPR farming campaign", + "0x3cf57b232feda914d81261d4c0feb7a510b7858c107696af862a37601dc687f4": "0xKLOM.eth Arbitrum Gas and ETHDenver Stipend", + "0x3e6215137546560a24cc54bba94bba1ef3f3f348842287898f085eaa66e324d8": "Test Proposal to Withdraw LP Tokens from WETH/wxDai pool on Swapr [1/2]", + "0x3f131d40fb2c9e0d8d771ceeaa0fea5919261536152093fc0d02e8bd79e50438": "Approve Swapr farming campaign for COW/WETH pair", + "0x40f50475fca7988cf84c26733151cb2581f4ff6cdbced672ef8160d8679df6d1": "Provision 150 WETH and 390k xDai to Swapr", + "0x40f8b0847efd72688758d6ab7c58a84132bc5e68c57d91c8be1314bd2160e717": "Nathan, contributor proposal 3/5/22-4/30/22", + "0x43f857b195320e0464f53d8ecb33aab669dfe0e7f263419aa4ac2d10da193125": "Tammy -- Vested DXD Request (Dec. '20 - March '21)", + "0x477bd183f2a90a20252e183f5956266555659d392ed11266c39ce27c9c266cd9": "AGVE / XDAI Epoch 14 - 15 SWPR farming campaign", + "0x4bc42058824c4ca5f8cef6dd2b78716f54e9fa94dc021370009dffbcffa312b1": "Provision 150 WETH and 450k xDai to Swapr", + "0x4fd9fc08411a92fff1810b338806529a16d5a6b548df82c6b02c1d0a0b7aeb42": "HND / XDAI farming campaign", + "0x51e16df1dd0a6de9bacb6955e39366140160023e9306ac9a6268353ec6b2918c": "SkyMine Labs - Stipend and Reimbursement around ETHDenver", + "0x53fdeca53c7c6440f9006964293db70ac6d297dd2199043e5325f82882176fb2": "Dave - Stipend for Devconnect Amsterdam", + "0x58d1cf014cff7f19f921f4345bcc1ed9e3cbab9e6e6bee224c024c202fa5e0e2": "Test Proposal to Withdraw LP Tokens from WETH/xDai pool on Swapr [2/2]", + "0x59b36d189960953e0bce203532af7f717575387eeb0138d70b4d1fdbf743227a": "Milan V. Contributor Proposal [1/3/2022 - 14/3/2022]", + "0x5b7e7cdab1cc23e4c78e93cedef86c59278259dd5206d2674904fc1eae1a40e7": "Borisblock — Contributor Proposal [07/01/22 - 07/03/22] 2/2", + "0x5d258180fc5922f69c09de3963d3ff0688d4ab0a9a1b4763a33be0d33edd47ab": "xDXD Buyback Order #170 for 10.03 WETH", + "0x5eaa2e8fa9d38e928218a5f5aa23591cae0bbed5789710083e75e7647898f847": " Trade 50k WXDAI to USDC ", + "0x635cb345ad6242ef248cd4d1af4a6f4dfd279577d853db0f54a6461bf1d70dc3": "DXD / WETH Epoch 12 - 13 SWPR farming campaign", + "0x67a488fb18db9ffdd1f9af4d8c68bad02379a96595adba1808a5ca0d70d1adc1": " Provision 100 WETH and 260k xDai to Swapr ", + "0x6ae455e54c9684d3712476809ce97289cca71742160408a7d72c298fc9638663": "dlabs REP sync ", + "0x6c8eba77df089d83821b6dfda6dddb2327593d1fd3933d26a48c7baecb1db6c8": "Test Proposal to Withdraw LP Tokens from relayer to treasury", + "0x73e3ed3be86120677b2dd93401ce3c729f304bac6ba6b2164de0cdb9456882d5": "[Etherlabs AB] Payment Proposal [March 2022]", + "0x7571912e7a5c4b166d74468d44121bfa259a275ddc5124442708f5ba3dd55f7b": "Swap 34k WXDAI to USDC", + "0x7648bc00cdd1b9b46ebb928538aafd693bd300a2122f2215f0c7622671f39b40": "Luchux - Payment Proposal 15th Dec 2021 - 25th Jan 2022. ", + "0x7bc28c23bf1035d22b82b5b393f5dd7fc3280fa0e3a8e3d42da8647b62969807": "DXdao Discord Q4 REP Boost for Bodo#0453", + "0x80121caeb85c037f5fc606f489a4cff12622f6c05b0d85aa2ce8ce4469f5e3b2": "Second and Third Payments to Team Omega for Audit of DXgovernance", + "0x805a31ecf7626142b64e640d2475c5ce1cdd7b210df05f35fea4599cb44fc6c2": "HAUS / WETH Epoch 14 - 15 SWPR farming campaign", + "0x858bec2ad4bcd89c36c9380d4299b19be0512e2ab19b5ce961119a1c6ddc433b": "GNO / DXD Epoch 12 - 13 SWPR farming campaign", + "0x8bb1bef2c2e0a8c3dd8f45fae33cdcef0bf9063438427c3b35bb3436b7ba7dac": "GNO / XDAI Epoch 12 - 13 SWPR farming campaign", + "0x8c06598b21cce13359a28a4c52efdc3606103bd93340f27fc6061ffe50a4d3b4": "[DRAFT] Levotiate Contributor Proposal 10/01/2022 - 10/03/2022 - second half", + "0x8e744c18b25b75c4fa95881daa15bcd492ebd2ad3a16c212514c66e4064d0e50": "GNO / WETH Epoch 12 - 13 SWPR farming campaign", + "0x952d8b39ff8dd5f0620dbe426acb3d137d92819cbb0631c6f9de734ac1a93c9f": "[SWPR / XDAI] - Swap fee update to 1%", + "0x971bb957d01a52d68ac8d4a4ccf3e7fd0583e4528a665cfb6e99fa641da844c3": "AGVE / XDAI Epoch 12 - 13 SWPR farming campaign", + "0x982e4dcea86d80a72ea6da2dab6730e34b409bee35788b414ff032d265b03f9b": "Level K - Reimbursements for ETHDenver Expenses", + "0xa57ea0f9ba56d6677b71ca9ac705cdab6ae170a081d5329fad315c8d44f2fb49": "2nd Test Proposal to Withdraw LP Tokens from WETH/wxDai pool on Swapr", + "0xa7ae51e589abdda68f082f0ea3f1f51e8be0fa04fd1c75a5f45c855f5cfe97d5": "SWPR / XDAI Epoch 12 - 13 SWPR farming campaign", + "0xad3f172fe7897c78d8ee7aac2f67d32c9c39d00dae858c619e4bf268ccddb80c": "DXAmsterdam Contributor Stipend", + "0xb1c9bf50e3b89be56279e9576b6c1e2516434d9abf7a8924d638377e858b4af7": "AugustoL Ferburary 2022 Work Payout", + "0xb5c6c20600d0324cbe600cfc1874fa1570e58202130b032e9f7f5643f375aa1b": "December 2021", + "0xbb75003e90205f0b732008521af4b531e0774f851981dc02d480d3566f05b871": "Smute - Contributor Proposal ", + "0xbc458ca8240faacbdee78b4efb321789a875ad55ded1cfc5df2129159694089e": "HND / WETH Epoch 12 - 13 SWPR farming campaign", + "0xbc9570eb252d3c72b16f58e79f8f26ca081930db44be408320bf12630564a401": "MAI / XDAI Epoch 12 - 13 SWPR farming campaign", + "0xbfd76fa4faa658ce026a54fbb8baea741014cdcec964f7b7584ee71c52aa87c5": "[Swap Fee] - Update swap fee of DXD / WETH to 0.6%", + "0xcba81e2fa2af07bdfa3a68496839a4ef722747769e95a9b520afd1a3574e186d": "Swapr HND/WETH farming campaign cancelation", + "0xcdee5c66dc09174bbef55341eca5e917e8f9e1a5b4f0df03ee85acf23eb30fff": "AllyQ Contributor Stipend ETHDenver February 2022", + "0xcf22139313ac001366712573f84c680c2c69fbd60ac0fe0ef128a9aa9f37af94": "AugustoL - Reimbursements for ETHDenver Expenses", + "0xd0555152a26164e57d55484c0305f66fee84cd72967f7bc2f6cc4235216b8110": "Madusha - Contributor Proposal [01/03/2022 - 30/04/2022] - First Half", + "0xd4bb2bafc24e0fe9b1b1b58c08306e4dc3fce5a5ffd0dad4957bf879ef1c0a76": "[Etherlabs AB] Payment Proposal [ Jan / Feb / March 2022] - 2/2", + "0xdcc56db033ceaf0e8cba81b6583d7a0e7034572520b582535c20c78d29cdbd2f": "CRV / WETH Epoch 14 - 15 SWPR farming campaign", + "0xe2de5a2142a5e08a3a7513c758fe0955e52c3b13bf3c224b4cfdc32cbe585bed": "RICE / XDAI Epoch 12 - 13 SWPR farming campaign", + "0xe38ea8f4f843920e4f450ff0cfbc66d6158543a546119b72ce8493b732de007d": "Milton - ETH Denver Stipend", + "0xe41ab7ec3ac9cb672043efb936ca9993d5ca2d97c6aaa14e61945faacc65e2d6": "Violet Worker Proposal 31/01/2022-31/03/2022 2/2", + "0xea97c70d686535ad265bfee1396e629e85edb5d585055a60b8655cc37e542e22": "Send HNDXDAI TVL Carrot to Liquidity mining relayer", + "0xf3d30c8e23ad26f4e9a75dc4cd71fa6a08a4e665e52791f97726efb90f0da8a5": "Alchemy API Expenses", + "0xf4c3ff2d5bd8d2111491052da99755d12b5ae3989ec061f16ef1651726c8a739": "Vangrim Contributor Proposal [12 March - 11 May (2022)]", + "0xf6941ec63a38b5f2c6282f7629bb7c1ccccd45f1be11902d0fb0b077c3cc96d1": "Send 14.9 WETH to GP Relayer for xDXD buyback #6", + "0xf85a3a5d75d9772e60807e4f5bb0d0ceb8f6c65d0c93b92b2d2e42cd6cb25505": "Luchux - payment proposal - 15th December 2021, 25th January 2022", + "0xf8bb4742b850198584c7bee39437151f5a6f6987dd23c106080f437002d2a155": "[DRAFT] Levotiate Contributor Proposal 11/03/2022 - 11/05/2022", + "0xf9d1887b1308e51583c8064da44b5487817b36811368dc0fe9fe78454ed5e740": "CRV / WETH Epoch 12 - 13 SWPR farming campaign", + "0xfb1795ba0a2b4ee12007fabc5f1847f344b40fbce8476727a648814e2e802261": "WETH / WBTC Epoch 12 - 13 SWPR farming campaign", + "0xff32174f3f2ff80955d5075937f72d07f5bc18364946cc2f54bc57c5f42a29ec": "xDXD Buyback Order #8" } \ No newline at end of file diff --git a/src/configs/rinkeby/config.json b/src/configs/rinkeby/config.json index b2ede7e0f1..15460865ef 100644 --- a/src/configs/rinkeby/config.json +++ b/src/configs/rinkeby/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 9234792, - "ipfsHash": "QmcQUUNkAy6V7PYH4wksHvhTmJbEiUGT6UyFik1FCAi8rE", - "toBlock": "10225316" + "ipfsHash": "QmTear8HaHRnNNTAu9RRMpdPdRxqKFmXTkqwGMgu1eKJFj", + "toBlock": "10525725" }, "contracts": { "avatar": "0x1A639b50D807ce7e61Dc9eeB091e6Cea8EcB1595", @@ -13,8 +13,8 @@ "utils": { "dxDaoNFT": "0xF7A2f72E8f6aeb884905a1fE515c1BB5752D342f", "dxdVestingFactory": "0xB5D42c8cA3B04479200E3782C41D99b46Cf8A6E1", - "multicall": "0x1FDD273e0B7E0d921Bda044Ebf64811428A79f15", - "guildRegistry": "0xA938DD7AEeF5F46BD9845882EAbA2ebF18e1b273" + "guildRegistry": "0xA938DD7AEeF5F46BD9845882EAbA2ebF18e1b273", + "multicall": "0x1FDD273e0B7E0d921Bda044Ebf64811428A79f15" }, "votingMachines": { "0xBa51391385A6bD5ECA0217d7e843B29227E72508": { diff --git a/src/configs/xdai/config.json b/src/configs/xdai/config.json index d3fafd1778..1668b9bebe 100644 --- a/src/configs/xdai/config.json +++ b/src/configs/xdai/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 13060713, - "ipfsHash": "QmfRWx3A2G3gL1sauTknmLUwiKP6NP1vWteEqPBXYV17uC", - "toBlock": "20806189" + "ipfsHash": "Qma5RVGFQiD9KhnQK1ZuVavT88zM7vSor68CzKiajsr9d7", + "toBlock": "21703404" }, "contracts": { "avatar": "0xe716EC63C5673B3a4732D22909b38d779fa47c3F", From b7dfbd03fdb6bd2eccd9a13945556d997060e9e9 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 13 Apr 2022 10:22:32 -0300 Subject: [PATCH 29/67] refactor(cacheService): simplify and remove duplicated code, add processProposal function --- src/services/CacheService.ts | 2217 +++++++++++++++------------------ src/stores/BlockchainStore.ts | 2 - src/utils/cache.ts | 69 +- 3 files changed, 1046 insertions(+), 1242 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 34453f7b09..0aa68bbd01 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1,5 +1,5 @@ import RootContext from '../contexts'; -import { getIPFSFile, NETWORK_NAMES } from '../utils'; +import { batchPromisesOntarget, getIPFSFile, NETWORK_NAMES } from '../utils'; import Web3 from 'web3'; import _ from 'lodash'; import { @@ -10,20 +10,15 @@ import { decodePermission, WalletSchemeProposalState, VotingMachineProposalState, - tryCacheUpdates, isWalletScheme, getEvents, getRawEvents, - sortEvents, executeMulticall, descriptionHashToIPFSHash, ipfsHashToDescriptionHash, getSchemeConfig, } from '../utils'; -import WalletScheme1_0JSON from '../contracts/WalletScheme1_0.json'; -import WalletScheme1_1JSON from '../contracts/WalletScheme1_1.json'; -import ContributionRewardJSON from '../contracts/ContributionReward.json'; -import TokenVestingJSON from '../contracts/TokenVesting.json'; + import { getContracts } from '../contracts'; const Hash = require('ipfs-only-hash'); @@ -56,6 +51,71 @@ export default class UtilsService { this.context = context; } + async getUpdatedCacheConfig( + networksConfig: { + [netwokId: number]: { + toBlock: number; + rpcUrl: string; + reset: boolean; + }; + }, + updateProposalTitles: boolean + ): Promise<{ + proposalTitles: { + [proposalId: string]: string; + }; + configHashes: { + [networkName: string]: string; + }; + configs: { + [networkName: string]: NetworkConfig; + }; + caches: { + [networkName: string]: DaoNetworkCache; + }; + }> { + const updatedCacheConfig = { + proposalTitles: proposalTitles, + configHashes: {}, + configs: {}, + caches: {}, + }; + // Update the cache and config for each network + for (let i = 0; i < Object.keys(networksConfig).length; i++) { + const networkId = Number(Object.keys(networksConfig)[i]); + const networkName = NETWORK_NAMES[networkId]; + + const cacheForNetwork = await this.buildCacheForNetwork( + new Web3(networksConfig[networkId].rpcUrl), + networkId, + networksConfig[networkId].toBlock, + networksConfig[networkId].reset + ); + + // Update the appConfig file that stores the hashes of the dapp config and network caches + updatedCacheConfig.configHashes[networkName] = cacheForNetwork.configHash; + updatedCacheConfig.configs[networkName] = cacheForNetwork.config; + updatedCacheConfig.caches[networkName] = cacheForNetwork.cache; + + // Get proposal titles + if (updateProposalTitles) { + this.context.notificationStore.setGlobalLoading( + true, + `Getting proposal titles form ipfs` + ); + updatedCacheConfig.proposalTitles = Object.assign( + updatedCacheConfig.proposalTitles, + await this.getProposalTitlesFromIPFS( + cacheForNetwork.cache, + proposalTitles + ) + ); + } + } + + return updatedCacheConfig; + } + async buildCacheForNetwork( web3: Web3, chainId: number, @@ -119,13 +179,11 @@ export default class UtilsService { } // Set block range for the script to run, if cache to block is set that value is used, if not we use last block - const fromBlock = networkCache.blockNumber; - - if (Number(fromBlock) < toBlock) { + if (Number(networkCache.blockNumber) + 1 < toBlock) { // The cache file is updated with the data that had before plus new data in the network cache file console.debug( 'Running cache script from block', - fromBlock, + networkCache.blockNumber + 1, 'to block', toBlock, 'in network', @@ -135,7 +193,6 @@ export default class UtilsService { this.context, networkCache, networkConfig.contracts, - fromBlock, toBlock, web3 ); @@ -157,122 +214,38 @@ export default class UtilsService { }; } - async getUpdatedCacheConfig( - networksConfig: { - [netwokId: number]: { - toBlock: number; - rpcUrl: string; - reset: boolean; - }; - }, - updateProposalTitles: boolean - ): Promise<{ - proposalTitles: { - [proposalId: string]: string; - }; - configHashes: { - [networkName: string]: string; - }; - configs: { - [networkName: string]: NetworkConfig; - }; - caches: { - [networkName: string]: DaoNetworkCache; - }; - }> { - const updatedCacheConfig = { - proposalTitles: proposalTitles, - configHashes: {}, - configs: {}, - caches: {}, - }; - // Update the cache and config for each network - for (let i = 0; i < Object.keys(networksConfig).length; i++) { - const networkId = Number(Object.keys(networksConfig)[i]); - const networkName = NETWORK_NAMES[networkId]; - - const cacheForNetwork = await this.buildCacheForNetwork( - new Web3(networksConfig[networkId].rpcUrl), - networkId, - networksConfig[networkId].toBlock, - networksConfig[networkId].reset - ); - - // Update the appConfig file that stores the hashes of the dapp config and network caches - updatedCacheConfig.configHashes[networkName] = cacheForNetwork.configHash; - updatedCacheConfig.configs[networkName] = cacheForNetwork.config; - updatedCacheConfig.caches[networkName] = cacheForNetwork.cache; - - // Get proposal titles - if (updateProposalTitles) { - this.context.notificationStore.setGlobalLoading( - true, - `Getting proposal titles form ipfs` - ); - updatedCacheConfig.proposalTitles = Object.assign( - updatedCacheConfig.proposalTitles, - await this.getProposalTitlesFromIPFS( - cacheForNetwork.cache, - proposalTitles - ) - ); - } - } - - return updatedCacheConfig; - } - async getUpdatedCache( context: RootContext, networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, - fromBlock: number, + networkContracts: NetworkContracts, toBlock: number, - web3: any + web3: Web3 ): Promise { const notificationStore = context.notificationStore; - + const fromBlock = networkCache.blockNumber + 1; console.debug(`[CACHE UPDATE] from ${fromBlock} to ${toBlock}`); - const networkWeb3Contracts = await getContracts( - networkContractsConfig, - web3 - ); + const networkWeb3Contracts = await getContracts(networkContracts, web3); notificationStore.setGlobalLoading( true, `Collecting reputation and governance events in blocks ${fromBlock} - ${toBlock}` ); - networkCache = await tryCacheUpdates( + networkCache = await batchPromisesOntarget( [ this.updateReputationEvents( networkCache, networkWeb3Contracts.reputation, - fromBlock, toBlock, web3 ), this.updateVotingMachineEvents( networkCache, - networkContractsConfig, - fromBlock, - toBlock, - web3 - ), - this.updatePermissionRegistry( - networkCache, - networkContractsConfig, - fromBlock, - toBlock, - web3 - ), - this.updateVestingContracts( - networkCache, - networkContractsConfig, - fromBlock, + networkContracts, toBlock, web3 ), + this.updateSchemes(networkCache, networkContracts, toBlock, web3), ], networkCache ); @@ -282,12 +255,17 @@ export default class UtilsService { `Updating scheme data in blocks ${fromBlock} - ${toBlock}` ); - networkCache = await tryCacheUpdates( + networkCache = await batchPromisesOntarget( [ - this.updateSchemes( + this.updatePermissionRegistry( networkCache, - networkContractsConfig, - fromBlock, + networkContracts, + toBlock, + web3 + ), + this.updateVestingContracts( + networkCache, + networkContracts, toBlock, web3 ), @@ -300,16 +278,8 @@ export default class UtilsService { `Collecting proposals in blocks ${fromBlock} - ${toBlock}` ); - networkCache = await tryCacheUpdates( - [ - this.updateProposals( - networkCache, - networkContractsConfig, - fromBlock, - toBlock, - web3 - ), - ], + networkCache = await batchPromisesOntarget( + [this.updateProposals(networkCache, networkContracts, toBlock, web3)], networkCache ); @@ -324,6 +294,22 @@ export default class UtilsService { // console.debug(proposalId, mutableData, cacheData); // }) + // Sort cache data, so the IPFS hash is consistent + Object.keys(networkCache.schemes).forEach(schemeId => { + networkCache.schemes[schemeId].proposalIds.sort(); + networkCache.schemes[schemeId].newProposalEvents.sort((a, b) => + a.proposalId.localeCompare(b.proposalId) + ); + }); + networkCache.proposals = Object.keys(networkCache.proposals) + .sort() + .reduce((obj, key) => { + obj[key] = networkCache.proposals[key]; + return obj; + }, {}); + networkCache.ipfsHashes = _.uniqBy(networkCache.ipfsHashes, 'name'); + networkCache.ipfsHashes.sort((a, b) => a.name.localeCompare(b.name)); + return networkCache; } @@ -331,14 +317,18 @@ export default class UtilsService { async updateReputationEvents( networkCache: DaoNetworkCache, reputation: any, - fromBlock: number, toBlock: number, - web3: any + web3: Web3 ): Promise { if (!networkCache.reputation.events) networkCache.reputation.events = []; - - let reputationEvents = sortEvents( - await getEvents(web3, reputation, fromBlock, toBlock, 'allEvents') + const fromBlock = networkCache.blockNumber + 1; + + let reputationEvents = await getEvents( + web3, + reputation, + fromBlock, + toBlock, + 'allEvents' ); reputationEvents.map(reputationEvent => { @@ -382,15 +372,11 @@ export default class UtilsService { // Update all voting machines async updateVotingMachineEvents( networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, - fromBlock: number, + networkContracts: NetworkContracts, toBlock: number, - web3: any + web3: Web3 ): Promise { - const networkWeb3Contracts = await getContracts( - networkContractsConfig, - web3 - ); + const networkWeb3Contracts = await getContracts(networkContracts, web3); await Promise.all( Object.keys(networkWeb3Contracts.votingMachines).map( @@ -418,9 +404,8 @@ export default class UtilsService { networkCache = await this.updateVotingMachine( networkCache, - networkContractsConfig, + networkContracts, votingMachine, - fromBlock, toBlock, web3 ); @@ -430,21 +415,25 @@ export default class UtilsService { return networkCache; } - // Update all voting machine information, events, token and voting parameters used. + + // Update a voting machine information, events, token and voting parameters used. async updateVotingMachine( networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, + networkContracts: NetworkContracts, votingMachine: any, - fromBlock: number, toBlock: number, - web3: any + web3: Web3 ): Promise { - let newVotingMachineEvents = sortEvents( - await getEvents(web3, votingMachine, fromBlock, toBlock, 'allEvents') - ); - const avatarAddress = web3.utils.toChecksumAddress( - networkContractsConfig.avatar + const fromBlock = networkCache.blockNumber + 1; + + let newVotingMachineEvents = await getEvents( + web3, + votingMachine, + fromBlock, + toBlock, + 'allEvents' ); + const avatarAddress = web3.utils.toChecksumAddress(networkContracts.avatar); const votingMachineEventsInCache = networkCache.votingMachines[votingMachine._address].events; newVotingMachineEvents.map(votingMachineEvent => { @@ -626,24 +615,20 @@ export default class UtilsService { // Gets all the events form the permission registry and stores the permissions set. async updatePermissionRegistry( networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, - fromBlock: number, + networkContracts: NetworkContracts, toBlock: number, - web3: any + web3: Web3 ): Promise { - const networkWeb3Contracts = await getContracts( - networkContractsConfig, - web3 - ); + const networkWeb3Contracts = await getContracts(networkContracts, web3); + const fromBlock = networkCache.blockNumber + 1; + if (networkWeb3Contracts.permissionRegistry._address !== ZERO_ADDRESS) { - let permissionRegistryEvents = sortEvents( - await getEvents( - web3, - networkWeb3Contracts.permissionRegistry, - fromBlock, - toBlock, - 'allEvents' - ) + let permissionRegistryEvents = await getEvents( + web3, + networkWeb3Contracts.permissionRegistry, + fromBlock, + toBlock, + 'allEvents' ); permissionRegistryEvents.map(permissionRegistryEvent => { @@ -684,25 +669,21 @@ export default class UtilsService { async updateVestingContracts( networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, - fromBlock: number, + networkContracts: NetworkContracts, toBlock: number, - web3: any + web3: Web3 ): Promise { - const networkWeb3Contracts = await getContracts( - networkContractsConfig, - web3 - ); + const networkWeb3Contracts = await getContracts(networkContracts, web3); + const fromBlock = networkCache.blockNumber + 1; + if (networkWeb3Contracts.vestingFactory) { try { - const vestingFactoryEvents = sortEvents( - await getEvents( - web3, - networkWeb3Contracts.vestingFactory, - fromBlock, - toBlock, - 'allEvents' - ) + const vestingFactoryEvents = await getEvents( + web3, + networkWeb3Contracts.vestingFactory, + fromBlock, + toBlock, + 'allEvents' ); console.debug( @@ -711,35 +692,65 @@ export default class UtilsService { ); for (let event of vestingFactoryEvents) { - const tokenVestingContract = await new web3.eth.Contract( - TokenVestingJSON.abi, - event.returnValues.vestingContractAddress - ); const callsToExecute = [ - [tokenVestingContract, 'beneficiary', []], - [tokenVestingContract, 'cliff', []], - [tokenVestingContract, 'duration', []], - [tokenVestingContract, 'owner', []], - [tokenVestingContract, 'start', []], - [tokenVestingContract, 'isOwner', []], - [tokenVestingContract, 'revocable', []], + [ + event.returnValues.vestingContractAddress, + 'beneficiary()', + [], + ['address'], + ], + [ + event.returnValues.vestingContractAddress, + 'cliff()', + [], + ['uint256'], + ], + [ + event.returnValues.vestingContractAddress, + 'duration()', + [], + ['uint256'], + ], + [ + event.returnValues.vestingContractAddress, + 'owner()', + [], + ['address'], + ], + [ + event.returnValues.vestingContractAddress, + 'start()', + [], + ['uint256'], + ], + [ + event.returnValues.vestingContractAddress, + 'isOwner()', + [], + ['bool'], + ], + [ + event.returnValues.vestingContractAddress, + 'revocable()', + [], + ['bool'], + ], ]; const callsResponse = await executeMulticall( - web3, networkWeb3Contracts.multicall, callsToExecute ); const tokenContractInfo = { address: event.returnValues.vestingContractAddress, - beneficiary: callsResponse.decodedReturnData[0], - cliff: callsResponse.decodedReturnData[1], - duration: callsResponse.decodedReturnData[2], - owner: callsResponse.decodedReturnData[3], - start: callsResponse.decodedReturnData[4], - isOwner: callsResponse.decodedReturnData[5], - revocable: callsResponse.decodedReturnData[6], + beneficiary: callsResponse.decodedReturnData[0][0], + cliff: callsResponse.decodedReturnData[1][0], + duration: callsResponse.decodedReturnData[2][0], + owner: callsResponse.decodedReturnData[3][0], + start: callsResponse.decodedReturnData[4][0], + isOwner: callsResponse.decodedReturnData[5][0], + revocable: callsResponse.decodedReturnData[6][0], }; networkCache.vestingContracts = [ @@ -758,26 +769,22 @@ export default class UtilsService { // Update all the schemes information async updateSchemes( networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, - fromBlock: number, + networkContracts: NetworkContracts, toBlock: number, - web3: any + web3: Web3 ): Promise { - const networkWeb3Contracts = await getContracts( - networkContractsConfig, - web3 - ); + const networkWeb3Contracts = await getContracts(networkContracts, web3); + const fromBlock = networkCache.blockNumber + 1; // Get all the events from the Controller - let controllerEvents = sortEvents( - await getEvents( - web3, - networkWeb3Contracts.controller, - fromBlock, - toBlock, - 'allEvents' - ) + let controllerEvents = await getEvents( + web3, + networkWeb3Contracts.controller, + fromBlock, + toBlock, + 'allEvents' ); + // Go over all controller events and add update or remove schemes depending on the event for ( let controllerEventsIndex = 0; @@ -791,10 +798,7 @@ export default class UtilsService { // Add or update the scheme information, // register scheme is used to add a scheme or update the parametersHash of an existent one if (controllerEvent.event === 'RegisterScheme') { - const schemeTypeData = getSchemeConfig( - networkContractsConfig, - schemeAddress - ); + const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); console.debug( 'Register Scheme event for ', @@ -810,25 +814,25 @@ export default class UtilsService { let callsToExecute = [ [ - networkWeb3Contracts.controller, - 'getSchemePermissions', + controllerAddress, + 'getSchemePermissions(address,address)', [schemeAddress, networkWeb3Contracts.avatar._address], + ['bytes4'], ], [ - networkWeb3Contracts.controller, - 'getSchemeParameters', + controllerAddress, + 'getSchemeParameters(address,address)', [schemeAddress, networkWeb3Contracts.avatar._address], + ['bytes32'], ], ]; if (schemeType === 'WalletScheme') { - const walletSchemeContract = await new web3.eth.Contract( - WalletScheme1_0JSON.abi, - schemeAddress - ); - const walletSchemeType = await walletSchemeContract.methods - .SCHEME_TYPE() - .call(); + const walletSchemeType = ( + await executeMulticall(networkWeb3Contracts.multicall, [ + [schemeAddress, 'SCHEME_TYPE()', [], ['string']], + ]) + ).decodedReturnData[0][0]; schemeType = walletSchemeType; if (schemeType == 'Wallet Scheme v1') @@ -836,68 +840,87 @@ export default class UtilsService { switch (schemeType) { case 'Wallet Scheme v1.0': - callsToExecute.push([walletSchemeContract, 'votingMachine', []]); callsToExecute.push([ - walletSchemeContract, - 'controllerAddress', + schemeAddress, + 'votingMachine()', [], + ['address'], ]); - callsToExecute.push([walletSchemeContract, 'schemeName', []]); callsToExecute.push([ - walletSchemeContract, - 'maxSecondsForExecution', + schemeAddress, + 'controllerAddress()', [], + ['address'], ]); callsToExecute.push([ - walletSchemeContract, - 'maxRepPercentageChange', + schemeAddress, + 'schemeName()', [], + ['string'], ]); - break; - default: - const walletSchemeContract1_1 = await new web3.eth.Contract( - WalletScheme1_1JSON.abi, - schemeAddress - ); callsToExecute.push([ - walletSchemeContract1_1, - 'votingMachine', + schemeAddress, + 'maxSecondsForExecution()', [], + ['uint256'], ]); callsToExecute.push([ - walletSchemeContract1_1, - 'doAvatarGenericCalls', + schemeAddress, + 'maxRepPercentageChange()', [], + ['uint256'], ]); - callsToExecute.push([walletSchemeContract1_1, 'schemeName', []]); + break; + default: callsToExecute.push([ - walletSchemeContract1_1, - 'maxSecondsForExecution', + schemeAddress, + 'votingMachine()', [], + ['address'], + ]); + callsToExecute.push([ + schemeAddress, + 'doAvatarGenericCalls()', + [], + ['bool'], + ]); + callsToExecute.push([ + schemeAddress, + 'schemeName()', + [], + ['string'], + ]); + callsToExecute.push([ + schemeAddress, + 'maxSecondsForExecution()', + [], + ['uint256'], ]); callsToExecute.push([ - walletSchemeContract1_1, - 'maxRepPercentageChange', + schemeAddress, + 'maxRepPercentageChange()', [], + ['uint256'], ]); break; } } + const callsResponse1 = await executeMulticall( - web3, networkWeb3Contracts.multicall, callsToExecute ); callsToExecute = []; const permissions = decodePermission( - callsResponse1.decodedReturnData[0] + callsResponse1.decodedReturnData[0][0] ); const paramsHash = - schemeTypeData.voteParams || callsResponse1.decodedReturnData[1]; + schemeTypeData.voteParams || callsResponse1.decodedReturnData[1][0]; const votingMachineAddress = - schemeTypeData.votingMachine || callsResponse1.decodedReturnData[2]; + schemeTypeData.votingMachine || + callsResponse1.decodedReturnData[2][0]; const votingMachine = networkWeb3Contracts.votingMachines[votingMachineAddress].contract; @@ -905,17 +928,17 @@ export default class UtilsService { if (schemeTypeData.type === 'WalletScheme') { switch (schemeType) { case 'Wallet Scheme v1.0': - controllerAddress = callsResponse1.decodedReturnData[3]; + controllerAddress = callsResponse1.decodedReturnData[3][0]; break; default: - controllerAddress = callsResponse1.decodedReturnData[3] + controllerAddress = callsResponse1.decodedReturnData[3][0] ? networkWeb3Contracts.controller._address : ZERO_ADDRESS; break; } - schemeName = callsResponse1.decodedReturnData[4]; - maxSecondsForExecution = callsResponse1.decodedReturnData[5]; - maxRepPercentageChange = callsResponse1.decodedReturnData[6]; + schemeName = callsResponse1.decodedReturnData[4][0]; + maxSecondsForExecution = callsResponse1.decodedReturnData[5][0]; + maxRepPercentageChange = callsResponse1.decodedReturnData[6][0]; } // Register the new voting parameters in the voting machine params @@ -969,38 +992,31 @@ export default class UtilsService { // This condition is added to skip the first scheme added (that is the dao creator account) controllerEvent.returnValues._sender !== schemeAddress ) { - const schemeTypeData = getSchemeConfig( - networkContractsConfig, - schemeAddress - ); - const votingMachine = - networkWeb3Contracts.votingMachines[ - networkCache.schemes[schemeAddress].votingMachine - ].contract; - + const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); console.debug('Unregister scheme event', schemeAddress, schemeTypeData); let callsToExecute = [ [ - votingMachine, - 'orgBoostedProposalsCnt', + networkCache.schemes[schemeAddress].votingMachine, + 'orgBoostedProposalsCnt(bytes32)', [ web3.utils.soliditySha3( schemeAddress, networkWeb3Contracts.avatar._address ), ], + ['uint256'], ], ]; if (isWalletScheme(networkCache.schemes[schemeAddress])) { callsToExecute.push([ - await new web3.eth.Contract(WalletScheme1_0JSON.abi, schemeAddress), - 'maxSecondsForExecution', + schemeAddress, + 'maxSecondsForExecution()', [], + ['uint256'], ]); } const callsResponse = await executeMulticall( - web3, networkWeb3Contracts.multicall, callsToExecute ); @@ -1008,51 +1024,46 @@ export default class UtilsService { const maxSecondsForExecution = isWalletScheme( networkCache.schemes[schemeAddress] ) - ? callsResponse.decodedReturnData[2] + ? callsResponse.decodedReturnData[2][0] : 0; // Update the scheme values a last time networkCache.schemes[schemeAddress].boostedProposals = - callsResponse.decodedReturnData[0]; + callsResponse.decodedReturnData[0][0]; networkCache.schemes[schemeAddress].maxSecondsForExecution = maxSecondsForExecution; networkCache.schemes[schemeAddress].registered = false; } } + // Update registered schemes await Promise.all( Object.keys(networkCache.schemes).map(async schemeAddress => { if (networkCache.schemes[schemeAddress].registered) { - const votingMachine = - networkWeb3Contracts.votingMachines[ - networkCache.schemes[schemeAddress].votingMachine - ].contract; - let callsToExecute = [ [ - votingMachine, - 'orgBoostedProposalsCnt', + networkCache.schemes[schemeAddress].votingMachine, + 'orgBoostedProposalsCnt(bytes32)', [ web3.utils.soliditySha3( schemeAddress, networkWeb3Contracts.avatar._address ), ], + ['uint256'], ], ]; if (isWalletScheme(networkCache.schemes[schemeAddress])) { callsToExecute.push([ - await new web3.eth.Contract( - WalletScheme1_0JSON.abi, - schemeAddress - ), - 'maxSecondsForExecution', + schemeAddress, + 'maxSecondsForExecution()', [], + ['uint256'], ]); callsToExecute.push([ - votingMachine, - 'boostedVoteRequiredPercentage', + networkCache.schemes[schemeAddress].votingMachine, + 'boostedVoteRequiredPercentage(bytes32,bytes32)', [ web3.utils.soliditySha3( schemeAddress, @@ -1060,10 +1071,10 @@ export default class UtilsService { ), networkCache.schemes[schemeAddress].paramsHash, ], + ['uint256'], ]); } const callsResponse = await executeMulticall( - web3, networkWeb3Contracts.multicall, callsToExecute ); @@ -1071,20 +1082,16 @@ export default class UtilsService { const maxSecondsForExecution = isWalletScheme( networkCache.schemes[schemeAddress] ) - ? callsResponse.decodedReturnData[1] + ? callsResponse.decodedReturnData[1][0] : 0; const boostedVoteRequiredPercentage = isWalletScheme( networkCache.schemes[schemeAddress] ) - ? web3.eth.abi.decodeParameters( - ['uint256'], - callsResponse.returnData[2] - )['0'] + ? callsResponse.decodedReturnData[2][0] : 0; - networkCache.schemes[schemeAddress].boostedProposals = - callsResponse.decodedReturnData[0]; + callsResponse.decodedReturnData[0][0]; networkCache.schemes[schemeAddress].maxSecondsForExecution = maxSecondsForExecution; networkCache.schemes[schemeAddress].boostedVoteRequiredPercentage = @@ -1099,33 +1106,23 @@ export default class UtilsService { // Update all the proposals information async updateProposals( networkCache: DaoNetworkCache, - networkContractsConfig: NetworkContracts, - fromBlock: number, + networkContracts: NetworkContracts, toBlock: number, - web3: any + web3: Web3 ): Promise { - const networkWeb3Contracts = await getContracts( - networkContractsConfig, - web3 - ); + const networkWeb3Contracts = await getContracts(networkContracts, web3); const avatarAddress = networkWeb3Contracts.avatar._address; const avatarAddressEncoded = web3.eth.abi.encodeParameter( 'address', avatarAddress ); + const fromBlock = networkCache.blockNumber + 1; // Get new proposals + // TO DO: Get only proposals from registered schemes, change registered to block number of unregisterScheme await Promise.all( Object.keys(networkCache.schemes).map(async schemeAddress => { - const schemeTypeData = getSchemeConfig( - networkContractsConfig, - schemeAddress - ); - const votingMachine = - networkWeb3Contracts.votingMachines[ - networkCache.schemes[schemeAddress].votingMachine - ].contract; - + const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); let schemeEvents = []; for (let i = 0; i < schemeTypeData.newProposalTopics.length; i++) { schemeEvents = schemeEvents.concat( @@ -1139,932 +1136,51 @@ export default class UtilsService { ); } - let schemeEventsBatchs = []; - let schemeEventsBatchsIndex = 0; - for (var i = 0; i < schemeEvents.length; i += 50) - schemeEventsBatchs.push(schemeEvents.slice(i, i + 50)); - - while (schemeEventsBatchsIndex < schemeEventsBatchs.length) { - try { - console.debug( - `Getting proposals of scheme ${schemeTypeData.name}: ${schemeAddress}, batch: ${schemeEventsBatchsIndex}` - ); - await Promise.all( - schemeEventsBatchs[schemeEventsBatchsIndex].map( - async schemeEvent => { - const proposalId = - schemeEvent.topics[1] === avatarAddressEncoded - ? web3.eth.abi.decodeParameter( - 'bytes32', - schemeEvent.topics[2] - ) - : web3.eth.abi.decodeParameter( - 'bytes32', - schemeEvent.topics[1] - ); - // Get all the proposal information from the scheme and voting machine - let callsToExecute = [ - [votingMachine, 'proposals', [proposalId]], - [votingMachine, 'voteStatus', [proposalId, 1]], - [votingMachine, 'voteStatus', [proposalId, 2]], - [votingMachine, 'proposalStatus', [proposalId]], - [votingMachine, 'getProposalTimes', [proposalId]], - ]; - - if (schemeTypeData.type === 'WalletScheme') { - callsToExecute.push([ - await new web3.eth.Contract( - WalletScheme1_0JSON.abi, - schemeAddress - ), - 'getOrganizationProposal', - [proposalId], - ]); - } else if (schemeTypeData.type === 'ContributionReward') { - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposalId, networkWeb3Contracts.avatar._address, 0], - ]); - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposalId, networkWeb3Contracts.avatar._address, 1], - ]); - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposalId, networkWeb3Contracts.avatar._address, 2], - ]); - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposalId, networkWeb3Contracts.avatar._address, 3], - ]); - } - - const callsResponse = await executeMulticall( - web3, - networkWeb3Contracts.multicall, - callsToExecute - ); - - const votingMachineProposalInfo = - web3.eth.abi.decodeParameters( - [ - { type: 'bytes32', name: 'organizationId' }, - { type: 'address', name: 'callbacks' }, - { type: 'uint256', name: 'state' }, - { type: 'uint256', name: 'winningVote' }, - { type: 'address', name: 'proposer' }, - { - type: 'uint256', - name: 'currentBoostedVotePeriodLimit', - }, - { type: 'bytes32', name: 'paramsHash' }, - { type: 'uint256', name: 'daoBountyRemain' }, - { type: 'uint256', name: 'daoBounty' }, - { type: 'uint256', name: 'totalStakes' }, - { type: 'uint256', name: 'confidenceThreshold' }, - { - type: 'uint256', - name: 'secondsFromTimeOutTillExecuteBoosted', - }, - ], - callsResponse.returnData[0] - ); - const positiveVotes = callsResponse.returnData[1]; - const negativeVotes = callsResponse.returnData[2]; - - const proposalStatusWithVotes = web3.eth.abi.decodeParameters( - ['uint256', 'uint256', 'uint256', 'uint256'], - callsResponse.returnData[3] - ); - const proposalTimes = callsResponse.decodedReturnData[4]; - - let schemeProposalInfo = { - to: [], - callData: [], - value: [], - state: WalletSchemeProposalState.Submitted, - title: '', - descriptionHash: '', - submittedTime: 0, - }; - let decodedProposer; - let creationLogDecoded; - - if (schemeTypeData.type === 'WalletScheme') { - schemeProposalInfo = web3.eth.abi.decodeParameters( - [ - { type: 'address[]', name: 'to' }, - { type: 'bytes[]', name: 'callData' }, - { type: 'uint256[]', name: 'value' }, - { type: 'uint256', name: 'state' }, - { type: 'string', name: 'title' }, - { type: 'string', name: 'descriptionHash' }, - { type: 'uint256', name: 'submittedTime' }, - ], - callsResponse.returnData[5] - ); - } else { - if (schemeTypeData.type === 'GenericMulticall') { - // event ProposalExecutedByVotingMachine( - // address indexed _avatar, - // bytes32 indexed _proposalId, - // int256 _param - // ); - const votingMachineExecutionEvent = - schemeProposalInfo.state === - WalletSchemeProposalState.Submitted - ? await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - [ - '0x25d4c89430c1f10c60c292556941e3e624ec1ec04972a5da46cee1b352429cbe', - avatarAddressEncoded, - proposalId, - ], - 10000000 - ) - : []; - - if ( - votingMachineExecutionEvent.length > 0 && - votingMachineExecutionEvent[0].data === - '0x0000000000000000000000000000000000000000000000000000000000000001' - ) - schemeProposalInfo.state = - WalletSchemeProposalState.Submitted; - else if (votingMachineExecutionEvent.length > 0) { - schemeProposalInfo.state = - WalletSchemeProposalState.Rejected; - } - - const executionEvent = await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - [ - '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', - avatarAddressEncoded, - proposalId, - ], - 10000000 - ); - if (executionEvent.length > 0) { - schemeProposalInfo.state = - WalletSchemeProposalState.ExecutionSucceded; - } - } else if (schemeTypeData.type === 'ContributionReward') { - if ( - callsResponse.decodedReturnData[5] > 0 || - callsResponse.decodedReturnData[6] > 0 || - callsResponse.decodedReturnData[7] > 0 || - callsResponse.decodedReturnData[8] > 0 - ) { - schemeProposalInfo.state = - WalletSchemeProposalState.ExecutionSucceded; - } else if ( - votingMachineProposalInfo.state === '1' || - votingMachineProposalInfo.state === '2' - ) { - schemeProposalInfo.state = - WalletSchemeProposalState.Rejected; - } else { - schemeProposalInfo.state = - WalletSchemeProposalState.Submitted; - } - } - - const transactionReceipt = - await web3.eth.getTransactionReceipt( - schemeEvent.transactionHash - ); - try { - schemeTypeData.newProposalTopics.map( - (newProposalTopic, i) => { - transactionReceipt.logs.map(log => { - if ( - log.topics[0] === - '0x75b4ff136cc5de5957574c797de3334eb1c141271922b825eb071e0487ba2c5c' - ) { - decodedProposer = web3.eth.abi.decodeParameters( - [ - { type: 'uint256', name: '_numOfChoices' }, - { type: 'address', name: '_proposer' }, - { type: 'bytes32', name: '_paramsHash' }, - ], - log.data - )._proposer; - } - if ( - !creationLogDecoded && - log.topics[0] === newProposalTopic[0] - ) { - creationLogDecoded = - web3.eth.abi.decodeParameters( - schemeTypeData.creationLogEncoding[i], - log.data - ); - if ( - creationLogDecoded._descriptionHash.length > - 0 && - creationLogDecoded._descriptionHash !== - ZERO_HASH - ) { - schemeProposalInfo.descriptionHash = - ipfsHashToDescriptionHash( - creationLogDecoded._descriptionHash - ); - } - } - }); - } - ); - } catch (error) { - console.error( - 'Error in getting proposal data from creation event', - error - ); - } - - if (schemeTypeData.type === 'SchemeRegistrar') { - schemeProposalInfo.to = [schemeTypeData.contractToCall]; - schemeProposalInfo.value = [0]; - - if (creationLogDecoded._parametersHash) { - schemeProposalInfo.callData = [ - web3.eth.abi.encodeFunctionCall( - { - name: 'registerScheme', - type: 'function', - inputs: [ - { type: 'address', name: '_scheme' }, - { type: 'bytes32', name: '_paramsHash' }, - { type: 'bytes4', name: '_permissions' }, - { type: 'address', name: '_avatar' }, - ], - }, - [ - creationLogDecoded['_scheme '], - creationLogDecoded._parametersHash, - creationLogDecoded._permissions, - avatarAddress, - ] - ), - ]; - } else { - schemeProposalInfo.callData = [ - web3.eth.abi.encodeFunctionCall( - { - name: 'unregisterScheme', - type: 'function', - inputs: [ - { type: 'address', name: '_scheme' }, - { type: 'address', name: '_avatar' }, - ], - }, - [creationLogDecoded['_scheme '], avatarAddress] - ), - ]; - } - } else if (schemeTypeData.type === 'ContributionReward') { - if (creationLogDecoded._reputationChange > 0) { - schemeProposalInfo.to.push( - schemeTypeData.contractToCall - ); - schemeProposalInfo.value.push(0); - schemeProposalInfo.callData.push( - web3.eth.abi.encodeFunctionCall( - { - name: 'mintReputation', - type: 'function', - inputs: [ - { type: 'uint256', name: '_amount' }, - { type: 'address', name: '_to' }, - { type: 'address', name: '_avatar' }, - ], - }, - [ - creationLogDecoded._reputationChange, - creationLogDecoded._beneficiary, - avatarAddress, - ] - ) - ); - } else if (creationLogDecoded._reputationChange < 0) { - schemeProposalInfo.to.push( - schemeTypeData.contractToCall - ); - schemeProposalInfo.value.push(0); - - // Remove the negative sign in the number - if (creationLogDecoded._reputationChange[0] == '-') - creationLogDecoded._reputationChange = - creationLogDecoded._reputationChange.substring(1); - - schemeProposalInfo.callData.push( - web3.eth.abi.encodeFunctionCall( - { - name: 'burnReputation', - type: 'function', - inputs: [ - { type: 'uint256', name: '_amount' }, - { type: 'address', name: '_from' }, - { type: 'address', name: '_avatar' }, - ], - }, - [ - creationLogDecoded._reputationChange, - creationLogDecoded._beneficiary, - avatarAddress, - ] - ) - ); - } - - if (creationLogDecoded._rewards[0] > 0) { - schemeProposalInfo.to.push( - schemeTypeData.contractToCall - ); - schemeProposalInfo.value.push(0); - schemeProposalInfo.callData.push( - web3.eth.abi.encodeFunctionCall( - { - name: 'mintTokens', - type: 'function', - inputs: [ - { type: 'uint256', name: '_amount' }, - { type: 'address', name: '_beneficiary' }, - { type: 'address', name: '_avatar' }, - ], - }, - [ - creationLogDecoded._rewards[0], - creationLogDecoded._beneficiary, - avatarAddress, - ] - ) - ); - } - - if (creationLogDecoded._rewards[1] > 0) { - schemeProposalInfo.to.push( - schemeTypeData.contractToCall - ); - schemeProposalInfo.value.push(0); - schemeProposalInfo.callData.push( - web3.eth.abi.encodeFunctionCall( - { - name: 'sendEther', - type: 'function', - inputs: [ - { type: 'uint256', name: '_amountInWei' }, - { type: 'address', name: '_to' }, - { type: 'address', name: '_avatar' }, - ], - }, - [ - creationLogDecoded._rewards[1], - creationLogDecoded._beneficiary, - avatarAddress, - ] - ) - ); - } - - if (creationLogDecoded._rewards[2] > 0) { - schemeProposalInfo.to.push( - schemeTypeData.contractToCall - ); - schemeProposalInfo.value.push(0); - schemeProposalInfo.callData.push( - web3.eth.abi.encodeFunctionCall( - { - name: 'externalTokenTransfer', - type: 'function', - inputs: [ - { type: 'address', name: '_externalToken' }, - { type: 'address', name: '_to' }, - { type: 'uint256', name: '_value' }, - { type: 'address', name: '_avatar' }, - ], - }, - [ - creationLogDecoded._externalToken, - creationLogDecoded._beneficiary, - creationLogDecoded._rewards[2], - avatarAddress, - ] - ) - ); - } - } else if (schemeTypeData.type === 'GenericScheme') { - schemeProposalInfo.to = [ - networkWeb3Contracts.controller._address, - ]; - schemeProposalInfo.value = [0]; - schemeProposalInfo.callData = [ - web3.eth.abi.encodeFunctionCall( - { - name: 'genericCall', - type: 'function', - inputs: [ - { type: 'address', name: '_contract' }, - { type: 'bytes', name: '_data' }, - { type: 'address', name: '_avatar' }, - { type: 'uint256', name: '_value' }, - ], - }, - [ - schemeTypeData.contractToCall, - creationLogDecoded._data, - avatarAddress, - creationLogDecoded._value, - ] - ), - ]; - } else if (schemeTypeData.type === 'GenericMulticall') { - for ( - let callIndex = 0; - callIndex < creationLogDecoded._contractsToCall.length; - callIndex++ - ) { - schemeProposalInfo.to.push( - networkWeb3Contracts.controller._address - ); - schemeProposalInfo.value.push(0); - schemeProposalInfo.callData.push( - web3.eth.abi.encodeFunctionCall( - { - name: 'genericCall', - type: 'function', - inputs: [ - { type: 'address', name: '_contract' }, - { type: 'bytes', name: '_data' }, - { type: 'address', name: '_avatar' }, - { type: 'uint256', name: '_value' }, - ], - }, - [ - creationLogDecoded._contractsToCall[callIndex], - creationLogDecoded._callsData[callIndex], - avatarAddress, - creationLogDecoded._values[callIndex], - ] - ) - ); - } - } - } - - // Register the new voting parameters in the voting machine params - if ( - !networkCache.votingMachines[votingMachine._address] - .votingParameters[votingMachineProposalInfo.paramsHash] - ) { - const votingParameters = await votingMachine.methods - .parameters(votingMachineProposalInfo.paramsHash) - .call(); - networkCache.votingMachines[ - votingMachine._address - ].votingParameters[votingMachineProposalInfo.paramsHash] = { - queuedVoteRequiredPercentage: - votingParameters.queuedVoteRequiredPercentage, - queuedVotePeriodLimit: - votingParameters.queuedVotePeriodLimit, - boostedVotePeriodLimit: - votingParameters.boostedVotePeriodLimit, - preBoostedVotePeriodLimit: - votingParameters.preBoostedVotePeriodLimit, - thresholdConst: votingParameters.thresholdConst, - limitExponentValue: votingParameters.limitExponentValue, - quietEndingPeriod: votingParameters.quietEndingPeriod, - proposingRepReward: votingParameters.proposingRepReward, - votersReputationLossRatio: - votingParameters.votersReputationLossRatio, - minimumDaoBounty: votingParameters.minimumDaoBounty, - daoBountyConst: votingParameters.daoBountyConst, - activationTime: votingParameters.activationTime, - }; - } - - networkCache.proposals[proposalId] = { - id: proposalId, - scheme: schemeAddress, - to: schemeProposalInfo.to, - title: schemeProposalInfo.title || '', - callData: schemeProposalInfo.callData, - values: schemeProposalInfo.value.map(value => bnum(value)), - stateInScheme: Number(schemeProposalInfo.state), - stateInVotingMachine: Number( - votingMachineProposalInfo.state - ), - descriptionHash: schemeProposalInfo.descriptionHash, - creationEvent: { - event: schemeEvent.event, - signature: schemeEvent.signature, - address: schemeEvent.address, - tx: schemeEvent.transactionHash, - blockNumber: schemeEvent.blockNumber, - timestamp: schemeEvent.timestamp, - transactionIndex: schemeEvent.transactionIndex, - logIndex: schemeEvent.logIndex, - }, - winningVote: votingMachineProposalInfo.winningVote, - proposer: decodedProposer - ? decodedProposer - : votingMachineProposalInfo.proposer, - currentBoostedVotePeriodLimit: - votingMachineProposalInfo.currentBoostedVotePeriodLimit, - paramsHash: votingMachineProposalInfo.paramsHash, - daoBountyRemain: bnum( - votingMachineProposalInfo.daoBountyRemain - ), - daoBounty: bnum(votingMachineProposalInfo.daoBounty), - confidenceThreshold: - votingMachineProposalInfo.confidenceThreshold, - secondsFromTimeOutTillExecuteBoosted: - votingMachineProposalInfo.secondsFromTimeOutTillExecuteBoosted, - submittedTime: bnum(proposalTimes[0]), - boostedPhaseTime: bnum(proposalTimes[1]), - preBoostedPhaseTime: bnum(proposalTimes[2]), - daoRedeemItsWinnings: - votingMachineProposalInfo.daoRedeemItsWinnings, - shouldBoost: false, - positiveVotes: bnum(positiveVotes), - negativeVotes: bnum(negativeVotes), - positiveStakes: bnum(proposalStatusWithVotes[2]), - negativeStakes: bnum(proposalStatusWithVotes[3]), - }; - - networkCache.schemes[schemeAddress].proposalIds.push( - proposalId - ); - networkCache.schemes[schemeAddress].newProposalEvents.push({ - proposalId: proposalId, - event: schemeEvent.event, - signature: schemeEvent.signature, - address: schemeEvent.address, - tx: schemeEvent.transactionHash, - blockNumber: schemeEvent.blockNumber, - timestamp: schemeEvent.timestamp, - transactionIndex: schemeEvent.transactionIndex, - logIndex: schemeEvent.logIndex, - }); - - if (schemeProposalInfo.descriptionHash.length > 1) { - networkCache.ipfsHashes.push({ - hash: descriptionHashToIPFSHash( - schemeProposalInfo.descriptionHash - ), - type: 'proposal', - name: proposalId, - }); - } - } - ) - ); - - schemeEventsBatchsIndex++; - } catch (error) { - console.error( - 'Error in getting proposal info of schemeEventsBatchs index', - schemeEventsBatchsIndex, - error + await batchPromisesOntarget( + schemeEvents.map(schemeEvent => { + const proposalId: string = + schemeEvent.topics[1] === avatarAddressEncoded + ? schemeEvent.topics[2] + : schemeEvent.topics[1]; + + return this.processProposal( + proposalId, + networkCache, + networkContracts, + web3, + fromBlock, + toBlock, + schemeEvent ); - } - } + }), + networkCache, + 50 + ); }) ); // Update existent active proposals - // @ts-ignore - const activeProposals = []; - Object.keys(networkCache.proposals).map(proposalId => { - if ( - networkCache.proposals[proposalId].stateInVotingMachine > - VotingMachineProposalState.Executed || - networkCache.proposals[proposalId].stateInScheme === - WalletSchemeProposalState.Submitted - ) - activeProposals.push(networkCache.proposals[proposalId]); - }); - - let activeProposalsBatch = []; - let activeProposalsBatchIndex = 0; - for (var i = 0; i < activeProposals.length; i += 5) - activeProposalsBatch.push(activeProposals.slice(i, i + 5)); - - while (activeProposalsBatchIndex < activeProposalsBatch.length) { - await Promise.all( - activeProposalsBatch[activeProposalsBatchIndex].map(async proposal => { - let retry = true; - while (retry) { - try { - const schemeAddress = networkCache.proposals[proposal.id].scheme; - const schemeTypeData = getSchemeConfig( - networkContractsConfig, - schemeAddress - ); - const votingMachine = - networkWeb3Contracts.votingMachines[ - networkCache.schemes[schemeAddress].votingMachine - ].contract; - - // Get all the proposal information from the scheme and voting machine - let callsToExecute = [ - [votingMachine, 'proposals', [proposal.id]], - [votingMachine, 'voteStatus', [proposal.id, 1]], - [votingMachine, 'voteStatus', [proposal.id, 2]], - [votingMachine, 'proposalStatus', [proposal.id]], - [votingMachine, 'getProposalTimes', [proposal.id]], - [votingMachine, 'shouldBoost', [proposal.id]], - ]; - - if (schemeTypeData.type === 'WalletScheme') { - callsToExecute.push([ - await new web3.eth.Contract( - WalletScheme1_0JSON.abi, - schemeAddress - ), - 'getOrganizationProposal', - [proposal.id], - ]); - } else if ( - schemeTypeData.type === 'ContributionReward' && - networkCache.proposals[proposal.id].stateInVotingMachine === - VotingMachineProposalState.Executed && - networkCache.proposals[proposal.id].stateInScheme === - WalletSchemeProposalState.Submitted - ) { - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposal.id, networkWeb3Contracts.avatar._address, 0], - ]); - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposal.id, networkWeb3Contracts.avatar._address, 1], - ]); - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposal.id, networkWeb3Contracts.avatar._address, 2], - ]); - callsToExecute.push([ - await new web3.eth.Contract( - ContributionRewardJSON.abi, - schemeAddress - ), - 'getRedeemedPeriods', - [proposal.id, networkWeb3Contracts.avatar._address, 3], - ]); - } - - const callsResponse = await executeMulticall( - web3, - networkWeb3Contracts.multicall, - callsToExecute - ); - - const votingMachineProposalInfo = web3.eth.abi.decodeParameters( - [ - { type: 'bytes32', name: 'organizationId' }, - { type: 'address', name: 'callbacks' }, - { type: 'uint256', name: 'state' }, - { type: 'uint256', name: 'winningVote' }, - { type: 'address', name: 'proposer' }, - { type: 'uint256', name: 'currentBoostedVotePeriodLimit' }, - { type: 'bytes32', name: 'paramsHash' }, - { type: 'uint256', name: 'daoBountyRemain' }, - { type: 'uint256', name: 'daoBounty' }, - { type: 'uint256', name: 'totalStakes' }, - { type: 'uint256', name: 'confidenceThreshold' }, - { - type: 'uint256', - name: 'secondsFromTimeOutTillExecuteBoosted', - }, - ], - callsResponse.returnData[0] - ); - const positiveVotes = callsResponse.returnData[1]; - const negativeVotes = callsResponse.returnData[2]; - - const proposalStatusWithVotes = web3.eth.abi.decodeParameters( - ['uint256', 'uint256', 'uint256', 'uint256'], - callsResponse.returnData[3] - ); - const proposalTimes = callsResponse.decodedReturnData[4]; - const proposalShouldBoost = callsResponse.decodedReturnData[5]; - - if (schemeTypeData.type === 'WalletScheme') { - networkCache.proposals[proposal.id].stateInScheme = Number( - web3.eth.abi.decodeParameters( - [ - { type: 'address[]', name: 'to' }, - { type: 'bytes[]', name: 'callData' }, - { type: 'uint256[]', name: 'value' }, - { type: 'uint256', name: 'state' }, - { type: 'string', name: 'title' }, - { type: 'string', name: 'descriptionHash' }, - { type: 'uint256', name: 'submittedTime' }, - ], - callsResponse.returnData[6] - ).state - ); - } else if ( - schemeTypeData.type === 'ContributionReward' && - networkCache.proposals[proposal.id].stateInVotingMachine === - VotingMachineProposalState.Executed && - networkCache.proposals[proposal.id].stateInScheme === - WalletSchemeProposalState.Submitted - ) { - if (schemeTypeData.type === 'ContributionReward') { - if ( - callsResponse.decodedReturnData[6] > 0 || - callsResponse.decodedReturnData[7] > 0 || - callsResponse.decodedReturnData[8] > 0 || - callsResponse.decodedReturnData[9] > 0 - ) { - networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.ExecutionSucceded; - } else if ( - votingMachineProposalInfo.state === '1' || - votingMachineProposalInfo.state === '2' - ) { - networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.Rejected; - } - } - } else if (schemeTypeData.type === 'GenericMulticall') { - // event ProposalExecutedByVotingMachine( - // address indexed _avatar, - // bytes32 indexed _proposalId, - // int256 _param - // ); - const votingMachineExecutionEvent = - networkCache.proposals[proposal.id].stateInScheme === - WalletSchemeProposalState.Submitted - ? await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - [ - '0x25d4c89430c1f10c60c292556941e3e624ec1ec04972a5da46cee1b352429cbe', - avatarAddressEncoded, - proposal.id, - ], - 10000000 - ) - : []; - - if ( - votingMachineExecutionEvent.length > 0 && - votingMachineExecutionEvent[0].data === - '0x0000000000000000000000000000000000000000000000000000000000000001' - ) - networkCache.proposals[proposal.id].stateInVotingMachine = - VotingMachineProposalState.Executed; - else if (votingMachineExecutionEvent.length > 0) { - networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.Rejected; - networkCache.proposals[proposal.id].stateInVotingMachine = - VotingMachineProposalState.Rejected; - } - - // event ProposalExecuted( - // address indexed _avatar, - // bytes32 indexed _proposalId - // ); - const executionEvent = - networkCache.proposals[proposal.id].stateInVotingMachine === - VotingMachineProposalState.Passed - ? await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - [ - '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', - avatarAddressEncoded, - proposal.id, - ], - 10000000 - ) - : []; - if (executionEvent.length > 0) { - networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.ExecutionSucceded; - } - } else if ( - networkCache.proposals[proposal.id].stateInVotingMachine === - VotingMachineProposalState.Executed - ) { - networkCache.proposals[proposal.id].stateInScheme = - WalletSchemeProposalState.ExecutionSucceded; - } - - networkCache.proposals[proposal.id].stateInVotingMachine = Number( - votingMachineProposalInfo.state - ); - networkCache.proposals[proposal.id].winningVote = - votingMachineProposalInfo.winningVote; - networkCache.proposals[ - proposal.id - ].currentBoostedVotePeriodLimit = - votingMachineProposalInfo.currentBoostedVotePeriodLimit; - networkCache.proposals[proposal.id].daoBountyRemain = bnum( - votingMachineProposalInfo.daoBountyRemain - ); - networkCache.proposals[proposal.id].daoBounty = bnum( - votingMachineProposalInfo.daoBounty - ); - networkCache.proposals[proposal.id].confidenceThreshold = - votingMachineProposalInfo.confidenceThreshold; - networkCache.proposals[ - proposal.id - ].secondsFromTimeOutTillExecuteBoosted = - votingMachineProposalInfo.secondsFromTimeOutTillExecuteBoosted; - networkCache.proposals[proposal.id].boostedPhaseTime = bnum( - proposalTimes[1] - ); - networkCache.proposals[proposal.id].preBoostedPhaseTime = bnum( - proposalTimes[2] - ); - networkCache.proposals[proposal.id].daoRedeemItsWinnings = - votingMachineProposalInfo.daoRedeemItsWinnings; - networkCache.proposals[proposal.id].shouldBoost = - proposalShouldBoost; - networkCache.proposals[proposal.id].positiveVotes = - bnum(positiveVotes); - networkCache.proposals[proposal.id].negativeVotes = - bnum(negativeVotes); - networkCache.proposals[proposal.id].positiveStakes = bnum( - proposalStatusWithVotes[2] - ); - networkCache.proposals[proposal.id].negativeStakes = bnum( - proposalStatusWithVotes[3] - ); - - retry = false; - } catch (e) { - console.error( - 'Error on updating proposal (trying again)', - proposal - ); - console.error(e); - retry = true; - } - } - }) - ); - - activeProposalsBatchIndex++; - } - - // Sort cache data, so the IPFS hash is consistent - Object.keys(networkCache.schemes).forEach(schemeId => { - networkCache.schemes[schemeId].proposalIds.sort(); - networkCache.schemes[schemeId].newProposalEvents.sort((a, b) => - a.proposalId.localeCompare(b.proposalId) - ); - }); - networkCache.proposals = Object.keys(networkCache.proposals) - .sort() - .reduce((obj, key) => { - obj[key] = networkCache.proposals[key]; - return obj; - }, {}); - networkCache.ipfsHashes = _.uniqBy(networkCache.ipfsHashes, 'name'); - networkCache.ipfsHashes.sort((a, b) => a.name.localeCompare(b.name)); + await batchPromisesOntarget( + //@ts-ignore + Object.keys(networkCache.proposals).map(proposalId => { + if ( + networkCache.proposals[proposalId].stateInVotingMachine > + VotingMachineProposalState.Executed || + networkCache.proposals[proposalId].stateInScheme === + WalletSchemeProposalState.Submitted + ) + return this.processProposal( + proposalId, + networkCache, + networkContracts, + web3, + fromBlock, + toBlock + ); + }), + networkCache, + 5 + ); return networkCache; } @@ -2150,4 +1266,653 @@ export default class UtilsService { return proposalTitles; } + + async processProposal( + proposalId: string, + networkCache: DaoNetworkCache, + networkContracts: NetworkContracts, + web3: Web3, + fromBlock: number, + toBlock: number, + creationEvent?: any + ): Promise { + const newProposal = !networkCache.proposals[proposalId]; + const schemeAddress = newProposal + ? creationEvent.address + : networkCache.proposals[proposalId].scheme; + const schemeOfProposal = networkCache.schemes[schemeAddress]; + const avatarAddress = networkCache.address; + const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); + const networkWeb3Contracts = await getContracts(networkContracts, web3); + const avatarAddressEncoded = web3.eth.abi.encodeParameter( + 'address', + avatarAddress + ); + + let callsToExecute: any[] = [ + [ + networkCache.schemes[schemeAddress].votingMachine, + 'proposals(bytes32)', + [proposalId], + [ + { type: 'bytes32', name: 'organizationId' }, + { type: 'address', name: 'callbacks' }, + { type: 'uint256', name: 'state' }, + { type: 'uint256', name: 'winningVote' }, + { type: 'address', name: 'proposer' }, + { + type: 'uint256', + name: 'currentBoostedVotePeriodLimit', + }, + { type: 'bytes32', name: 'paramsHash' }, + { type: 'uint256', name: 'daoBountyRemain' }, + { type: 'uint256', name: 'daoBounty' }, + { type: 'uint256', name: 'totalStakes' }, + { type: 'uint256', name: 'confidenceThreshold' }, + { + type: 'uint256', + name: 'secondsFromTimeOutTillExecuteBoosted', + }, + ], + ], + [ + networkCache.schemes[schemeAddress].votingMachine, + 'voteStatus(bytes32,uint256)', + [proposalId, 1], + ['uint256'], + ], + [ + networkCache.schemes[schemeAddress].votingMachine, + 'voteStatus(bytes32,uint256)', + [proposalId, 2], + ['uint256'], + ], + [ + networkCache.schemes[schemeAddress].votingMachine, + 'proposalStatus(bytes32)', + [proposalId], + ['uint256', 'uint256', 'uint256', 'uint256'], + ], + [ + networkCache.schemes[schemeAddress].votingMachine, + 'getProposalTimes(bytes32)', + [proposalId], + ['uint256', 'uint256', 'uint256'], + ], + [ + networkCache.schemes[schemeAddress].votingMachine, + 'shouldBoost(bytes32)', + [proposalId], + ['bool'], + ], + ]; + + if (schemeTypeData.type === 'ContributionReward') { + callsToExecute.push([ + schemeAddress, + 'getRedeemedPeriods(bytes32,address,uint256)', + [proposalId, schemeAddress, 0], + ['uint256'], + ]); + callsToExecute.push([ + schemeAddress, + 'getRedeemedPeriods(bytes32,address,uint256)', + [proposalId, avatarAddress, 1], + ['uint256'], + ]); + callsToExecute.push([ + schemeAddress, + 'getRedeemedPeriods(bytes32,address,uint256)', + [proposalId, avatarAddress, 2], + ['uint256'], + ]); + callsToExecute.push([ + schemeAddress, + 'getRedeemedPeriods(bytes32,address,uint256)', + [proposalId, avatarAddress, 3], + ['uint256'], + ]); + } else if (isWalletScheme(schemeOfProposal)) { + callsToExecute.push([ + schemeAddress, + 'getOrganizationProposal(bytes32)', + [proposalId], + [ + { type: 'address[]', name: 'to' }, + { type: 'bytes[]', name: 'callData' }, + { type: 'uint256[]', name: 'value' }, + { type: 'uint256', name: 'state' }, + { type: 'string', name: 'title' }, + { type: 'string', name: 'descriptionHash' }, + { type: 'uint256', name: 'submittedTime' }, + ], + ]); + } + + const callsResponse = await executeMulticall( + networkWeb3Contracts.multicall, + callsToExecute + ); + + const positiveVotes = callsResponse.decodedReturnData[1][0]; + const negativeVotes = callsResponse.decodedReturnData[2][0]; + const proposalTimes = callsResponse.decodedReturnData[4]; + + let schemeProposalInfo = { + to: [], + callData: [], + value: [], + state: WalletSchemeProposalState.Submitted, + title: '', + descriptionHash: '', + submittedTime: 0, + }; + let decodedProposer; + let creationLogDecoded; + + if (newProposal && isWalletScheme(schemeOfProposal)) { + // @ts-ignore + schemeProposalInfo.to = callsResponse.decodedReturnData[6].to; + schemeProposalInfo.callData = callsResponse.decodedReturnData[6].callData; + schemeProposalInfo.value = callsResponse.decodedReturnData[6].value; + schemeProposalInfo.state = callsResponse.decodedReturnData[6].state; + schemeProposalInfo.title = callsResponse.decodedReturnData[6].title; + schemeProposalInfo.descriptionHash = + callsResponse.decodedReturnData[6].descriptionHash; + schemeProposalInfo.submittedTime = + callsResponse.decodedReturnData[6].submittedTime; + } else if (isWalletScheme(schemeOfProposal)) { + schemeProposalInfo.state = callsResponse.decodedReturnData[6].state; + } else { + if (schemeOfProposal.type === 'GenericMulticall') { + // event ProposalExecutedByVotingMachine( + // address indexed _avatar, + // bytes32 indexed _proposalId, + // int256 _param + // ); + const votingMachineExecutionEvent = + schemeProposalInfo.state === WalletSchemeProposalState.Submitted + ? await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + [ + '0x25d4c89430c1f10c60c292556941e3e624ec1ec04972a5da46cee1b352429cbe', + avatarAddressEncoded, + proposalId, + ], + 10000000 + ) + : []; + + if ( + votingMachineExecutionEvent.length > 0 && + votingMachineExecutionEvent[0].data === + '0x0000000000000000000000000000000000000000000000000000000000000001' + ) + schemeProposalInfo.state = WalletSchemeProposalState.Submitted; + else if (votingMachineExecutionEvent.length > 0) { + schemeProposalInfo.state = WalletSchemeProposalState.Rejected; + } + + const executionEvent = await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + [ + '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', + avatarAddressEncoded, + proposalId, + ], + 10000000 + ); + if (executionEvent.length > 0) { + schemeProposalInfo.state = + WalletSchemeProposalState.ExecutionSucceded; + } + } else if (schemeOfProposal.type === 'ContributionReward') { + if ( + callsResponse.decodedReturnData[6][0] > 0 || + callsResponse.decodedReturnData[7][0] > 0 || + callsResponse.decodedReturnData[8][0] > 0 || + callsResponse.decodedReturnData[9][0] > 0 + ) { + schemeProposalInfo.state = + WalletSchemeProposalState.ExecutionSucceded; + } else if ( + callsResponse.decodedReturnData[0].state === '1' || + callsResponse.decodedReturnData[0].state === '2' + ) { + schemeProposalInfo.state = WalletSchemeProposalState.Rejected; + } else { + schemeProposalInfo.state = WalletSchemeProposalState.Submitted; + } + } + } + + // If the proposal is processed with a creation event it means that it has to be added to the cache + if (newProposal) { + if (creationEvent && !isWalletScheme(schemeOfProposal)) { + const transactionReceipt = await web3.eth.getTransactionReceipt( + creationEvent.transactionHash + ); + try { + schemeTypeData.newProposalTopics.map((newProposalTopic, i) => { + transactionReceipt.logs.map(log => { + if ( + log.topics[0] === + '0x75b4ff136cc5de5957574c797de3334eb1c141271922b825eb071e0487ba2c5c' + ) { + decodedProposer = web3.eth.abi.decodeParameters( + [ + { type: 'uint256', name: '_numOfChoices' }, + { type: 'address', name: '_proposer' }, + { type: 'bytes32', name: '_paramsHash' }, + ], + log.data + )._proposer; + } + if ( + !creationLogDecoded && + log.topics[0] === newProposalTopic[0] + ) { + creationLogDecoded = web3.eth.abi.decodeParameters( + schemeTypeData.creationLogEncoding[i], + log.data + ); + if ( + creationLogDecoded._descriptionHash.length > 0 && + creationLogDecoded._descriptionHash !== ZERO_HASH + ) { + schemeProposalInfo.descriptionHash = + ipfsHashToDescriptionHash( + creationLogDecoded._descriptionHash + ); + } + } + }); + }); + } catch (error) { + console.error( + 'Error in getting proposal data from creation event', + error + ); + } + } + + if (schemeTypeData.type === 'SchemeRegistrar') { + schemeProposalInfo.to = [schemeTypeData.contractToCall]; + schemeProposalInfo.value = [0]; + + if (creationLogDecoded._parametersHash) { + schemeProposalInfo.callData = [ + web3.eth.abi.encodeFunctionCall( + { + name: 'registerScheme', + type: 'function', + inputs: [ + { type: 'address', name: '_scheme' }, + { type: 'bytes32', name: '_paramsHash' }, + { type: 'bytes4', name: '_permissions' }, + { type: 'address', name: '_avatar' }, + ], + }, + [ + creationLogDecoded['_scheme '], + creationLogDecoded._parametersHash, + creationLogDecoded._permissions, + avatarAddress, + ] + ), + ]; + } else { + schemeProposalInfo.callData = [ + web3.eth.abi.encodeFunctionCall( + { + name: 'unregisterScheme', + type: 'function', + inputs: [ + { type: 'address', name: '_scheme' }, + { type: 'address', name: '_avatar' }, + ], + }, + [creationLogDecoded['_scheme '], avatarAddress] + ), + ]; + } + } else if (schemeTypeData.type === 'ContributionReward') { + if (creationLogDecoded._reputationChange > 0) { + schemeProposalInfo.to.push(schemeTypeData.contractToCall); + schemeProposalInfo.value.push(0); + schemeProposalInfo.callData.push( + web3.eth.abi.encodeFunctionCall( + { + name: 'mintReputation', + type: 'function', + inputs: [ + { type: 'uint256', name: '_amount' }, + { type: 'address', name: '_to' }, + { type: 'address', name: '_avatar' }, + ], + }, + [ + creationLogDecoded._reputationChange, + creationLogDecoded._beneficiary, + avatarAddress, + ] + ) + ); + } else if (creationLogDecoded._reputationChange < 0) { + schemeProposalInfo.to.push(schemeTypeData.contractToCall); + schemeProposalInfo.value.push(0); + + // Remove the negative sign in the number + if (creationLogDecoded._reputationChange[0] == '-') + creationLogDecoded._reputationChange = + creationLogDecoded._reputationChange.substring(1); + + schemeProposalInfo.callData.push( + web3.eth.abi.encodeFunctionCall( + { + name: 'burnReputation', + type: 'function', + inputs: [ + { type: 'uint256', name: '_amount' }, + { type: 'address', name: '_from' }, + { type: 'address', name: '_avatar' }, + ], + }, + [ + creationLogDecoded._reputationChange, + creationLogDecoded._beneficiary, + avatarAddress, + ] + ) + ); + } + + if (creationLogDecoded._rewards[0] > 0) { + schemeProposalInfo.to.push(schemeTypeData.contractToCall); + schemeProposalInfo.value.push(0); + schemeProposalInfo.callData.push( + web3.eth.abi.encodeFunctionCall( + { + name: 'mintTokens', + type: 'function', + inputs: [ + { type: 'uint256', name: '_amount' }, + { type: 'address', name: '_beneficiary' }, + { type: 'address', name: '_avatar' }, + ], + }, + [ + creationLogDecoded._rewards[0], + creationLogDecoded._beneficiary, + avatarAddress, + ] + ) + ); + } + + if (creationLogDecoded._rewards[1] > 0) { + schemeProposalInfo.to.push(schemeTypeData.contractToCall); + schemeProposalInfo.value.push(0); + schemeProposalInfo.callData.push( + web3.eth.abi.encodeFunctionCall( + { + name: 'sendEther', + type: 'function', + inputs: [ + { type: 'uint256', name: '_amountInWei' }, + { type: 'address', name: '_to' }, + { type: 'address', name: '_avatar' }, + ], + }, + [ + creationLogDecoded._rewards[1], + creationLogDecoded._beneficiary, + avatarAddress, + ] + ) + ); + } + + if (creationLogDecoded._rewards[2] > 0) { + schemeProposalInfo.to.push(schemeTypeData.contractToCall); + schemeProposalInfo.value.push(0); + schemeProposalInfo.callData.push( + web3.eth.abi.encodeFunctionCall( + { + name: 'externalTokenTransfer', + type: 'function', + inputs: [ + { type: 'address', name: '_externalToken' }, + { type: 'address', name: '_to' }, + { type: 'uint256', name: '_value' }, + { type: 'address', name: '_avatar' }, + ], + }, + [ + creationLogDecoded._externalToken, + creationLogDecoded._beneficiary, + creationLogDecoded._rewards[2], + avatarAddress, + ] + ) + ); + } + } else if (schemeTypeData.type === 'GenericScheme') { + schemeProposalInfo.to = [networkWeb3Contracts.controller._address]; + schemeProposalInfo.value = [0]; + schemeProposalInfo.callData = [ + web3.eth.abi.encodeFunctionCall( + { + name: 'genericCall', + type: 'function', + inputs: [ + { type: 'address', name: '_contract' }, + { type: 'bytes', name: '_data' }, + { type: 'address', name: '_avatar' }, + { type: 'uint256', name: '_value' }, + ], + }, + [ + schemeTypeData.contractToCall, + creationLogDecoded._data, + avatarAddress, + creationLogDecoded._value, + ] + ), + ]; + } else if (schemeTypeData.type === 'GenericMulticall') { + for ( + let callIndex = 0; + callIndex < creationLogDecoded._contractsToCall.length; + callIndex++ + ) { + schemeProposalInfo.to.push(networkWeb3Contracts.controller._address); + schemeProposalInfo.value.push(0); + schemeProposalInfo.callData.push( + web3.eth.abi.encodeFunctionCall( + { + name: 'genericCall', + type: 'function', + inputs: [ + { type: 'address', name: '_contract' }, + { type: 'bytes', name: '_data' }, + { type: 'address', name: '_avatar' }, + { type: 'uint256', name: '_value' }, + ], + }, + [ + creationLogDecoded._contractsToCall[callIndex], + creationLogDecoded._callsData[callIndex], + avatarAddress, + creationLogDecoded._values[callIndex], + ] + ) + ); + } + } + + // Register the new voting parameters in the voting machine params + if ( + !networkCache.votingMachines[ + networkCache.schemes[schemeAddress].votingMachine + ].votingParameters[callsResponse.decodedReturnData[0].paramsHash] + ) { + const votingParameters = ( + await executeMulticall(networkWeb3Contracts.multicall, [ + [ + networkCache.schemes[schemeAddress].votingMachine, + 'parameters(bytes32)', + [callsResponse.decodedReturnData[0].paramsHash], + [ + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'address', + ], + ], + ]) + ).decodedReturnData[0]; + networkCache.votingMachines[ + networkCache.schemes[schemeAddress].votingMachine + ].votingParameters[callsResponse.decodedReturnData[0].paramsHash] = { + queuedVoteRequiredPercentage: votingParameters[0], + queuedVotePeriodLimit: votingParameters[1], + boostedVotePeriodLimit: votingParameters[2], + preBoostedVotePeriodLimit: votingParameters[3], + thresholdConst: votingParameters[4], + limitExponentValue: votingParameters[5], + quietEndingPeriod: votingParameters[6], + proposingRepReward: votingParameters[7], + votersReputationLossRatio: votingParameters[8], + minimumDaoBounty: votingParameters[9], + daoBountyConst: votingParameters[10], + activationTime: votingParameters[11], + }; + } + + networkCache.proposals[proposalId] = { + id: proposalId, + scheme: schemeAddress, + to: schemeProposalInfo.to, + title: schemeProposalInfo.title || '', + callData: schemeProposalInfo.callData, + values: schemeProposalInfo.value.map(value => bnum(value)), + stateInScheme: Number(schemeProposalInfo.state), + stateInVotingMachine: Number(callsResponse.decodedReturnData[0].state), + descriptionHash: schemeProposalInfo.descriptionHash, + creationEvent: { + event: creationEvent.event, + signature: creationEvent.signature, + address: creationEvent.address, + tx: creationEvent.transactionHash, + blockNumber: creationEvent.blockNumber, + timestamp: creationEvent.timestamp, + transactionIndex: creationEvent.transactionIndex, + logIndex: creationEvent.logIndex, + }, + winningVote: callsResponse.decodedReturnData[0].winningVote, + proposer: decodedProposer + ? decodedProposer + : callsResponse.decodedReturnData[0].proposer, + currentBoostedVotePeriodLimit: + callsResponse.decodedReturnData[0].currentBoostedVotePeriodLimit, + paramsHash: callsResponse.decodedReturnData[0].paramsHash, + daoBountyRemain: bnum( + callsResponse.decodedReturnData[0].daoBountyRemain + ), + daoBounty: bnum(callsResponse.decodedReturnData[0].daoBounty), + confidenceThreshold: + callsResponse.decodedReturnData[0].confidenceThreshold, + secondsFromTimeOutTillExecuteBoosted: + callsResponse.decodedReturnData[0] + .secondsFromTimeOutTillExecuteBoosted, + submittedTime: bnum(proposalTimes[0]), + boostedPhaseTime: bnum(proposalTimes[1]), + preBoostedPhaseTime: bnum(proposalTimes[2]), + daoRedeemItsWinnings: + callsResponse.decodedReturnData[0].daoRedeemItsWinnings, + shouldBoost: callsResponse.decodedReturnData[5][0], + positiveVotes: bnum(positiveVotes), + negativeVotes: bnum(negativeVotes), + positiveStakes: bnum(callsResponse.decodedReturnData[3][2]), + negativeStakes: bnum(callsResponse.decodedReturnData[3][3]), + }; + + networkCache.schemes[schemeAddress].proposalIds.push(proposalId); + networkCache.schemes[schemeAddress].newProposalEvents.push({ + proposalId: proposalId, + event: creationEvent.event, + signature: creationEvent.signature, + address: creationEvent.address, + tx: creationEvent.transactionHash, + blockNumber: creationEvent.blockNumber, + timestamp: creationEvent.timestamp, + transactionIndex: creationEvent.transactionIndex, + logIndex: creationEvent.logIndex, + }); + + if (schemeProposalInfo.descriptionHash.length > 1) { + networkCache.ipfsHashes.push({ + hash: descriptionHashToIPFSHash(schemeProposalInfo.descriptionHash), + type: 'proposal', + name: proposalId, + }); + } + } else { + networkCache.proposals[proposalId].stateInScheme = Number( + schemeProposalInfo.state + ); + networkCache.proposals[proposalId].stateInVotingMachine = Number( + callsResponse.decodedReturnData[0].state + ); + networkCache.proposals[proposalId].winningVote = + callsResponse.decodedReturnData[0].winningVote; + networkCache.proposals[proposalId].currentBoostedVotePeriodLimit = + callsResponse.decodedReturnData[0].currentBoostedVotePeriodLimit; + networkCache.proposals[proposalId].daoBountyRemain = bnum( + callsResponse.decodedReturnData[0].daoBountyRemain + ); + networkCache.proposals[proposalId].daoBounty = bnum( + callsResponse.decodedReturnData[0].daoBounty + ); + networkCache.proposals[proposalId].confidenceThreshold = + callsResponse.decodedReturnData[0].confidenceThreshold; + networkCache.proposals[proposalId].secondsFromTimeOutTillExecuteBoosted = + callsResponse.decodedReturnData[0].secondsFromTimeOutTillExecuteBoosted; + networkCache.proposals[proposalId].boostedPhaseTime = bnum( + proposalTimes[1] + ); + networkCache.proposals[proposalId].preBoostedPhaseTime = bnum( + proposalTimes[2] + ); + networkCache.proposals[proposalId].daoRedeemItsWinnings = + callsResponse.decodedReturnData[0].daoRedeemItsWinnings; + networkCache.proposals[proposalId].shouldBoost = + callsResponse.decodedReturnData[5][0]; + networkCache.proposals[proposalId].positiveVotes = bnum(positiveVotes); + networkCache.proposals[proposalId].negativeVotes = bnum(negativeVotes); + networkCache.proposals[proposalId].positiveStakes = bnum( + callsResponse.decodedReturnData[3][2] + ); + networkCache.proposals[proposalId].negativeStakes = bnum( + callsResponse.decodedReturnData[3][3] + ); + } + return networkCache; + } } diff --git a/src/stores/BlockchainStore.ts b/src/stores/BlockchainStore.ts index 1b5436a905..dca9e65d60 100644 --- a/src/stores/BlockchainStore.ts +++ b/src/stores/BlockchainStore.ts @@ -127,7 +127,6 @@ export default class BlockchainStore { chainId ); - const fromBlock = lastCheckedBlockNumber + 1; const toBlock = blockNumber; const networkContracts = configStore.getNetworkContracts(); @@ -135,7 +134,6 @@ export default class BlockchainStore { this.context, networkCache, networkContracts, - fromBlock, toBlock, library ); diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 9c79ef731d..060db54859 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -1,8 +1,10 @@ +import { Contract } from 'ethers'; import _ from 'lodash'; import { MAX_BLOCKS_PER_EVENTS_FETCH } from './constants'; import { sleep } from './helpers'; const Web3 = require('web3'); +const web3 = new Web3(); export const getEvents = async function ( web3, @@ -59,7 +61,7 @@ export const getEvents = async function ( } } } - return events; + return sortEvents(events); }; export const getRawEvents = async function ( @@ -119,7 +121,7 @@ export const getRawEvents = async function ( } } } - return events; + return sortEvents(events); }; export const getTimestampOfEvents = async function (web3, events) { @@ -180,27 +182,30 @@ export const sortEvents = function (events) { ); }; -export const executeMulticall = async function (web3, multicall, calls) { +export const executeMulticall = async function ( + multicall: Contract, + calls: any[] +) { const rawCalls = calls.map(call => { + const functionParams = [...call[1].matchAll(/\(([^)]+)\)/g)] + .flat()[1] + ?.split(','); return [ - call[0]._address, - web3.eth.abi.encodeFunctionCall( - call[0]._jsonInterface.find(method => method.name === call[1]), - call[2] - ), + call[0], + web3.eth.abi.encodeFunctionSignature(call[1]) + + (functionParams + ? web3.eth.abi.encodeParameters(functionParams, call[2]).substring(2) + : ''), ]; }); - const { returnData } = await multicall.methods.aggregate(rawCalls).call(); return { returnData, decodedReturnData: returnData.map((callResult, i) => { - return web3.eth.abi.decodeParameters( - calls[i][0]._jsonInterface.find(method => method.name === calls[i][1]) - .outputs, - callResult - )['0']; + return calls[i][3].length > 0 + ? web3.eth.abi.decodeParameters(calls[i][3], callResult) + : ''; }), }; }; @@ -266,3 +271,39 @@ export async function tryCacheUpdates(promises, networkCache) { } return networkCache; } + +export async function batchPromisesOntarget( + promises, + targetObject, + maxPromisesPerTry = 0, + maxErrorsTry = 5 +) { + let promisesBatch = maxPromisesPerTry === 0 ? [promises] : []; + let promisesBatchIndex = 0; + let errorsTry = 0; + + if (maxPromisesPerTry > 0) + for (var i = 0; i < promises.length; i += maxPromisesPerTry) + promisesBatch.push(promises.slice(i, i + maxPromisesPerTry)); + + while ( + promisesBatchIndex < promisesBatch.length && + errorsTry < maxErrorsTry + ) { + try { + (await Promise.all(promisesBatch[promisesBatchIndex])).map( + targetObjectUpdated => { + targetObject = Object.assign(targetObject, targetObjectUpdated); + } + ); + promisesBatchIndex++; + } catch (e) { + console.error(e); + errorsTry++; + } finally { + if (errorsTry >= maxErrorsTry) + console.error('[BATCH PROMISES] (max errors reached)'); + } + } + return targetObject; +} From 7e1f523f3fe3271850a7f03c3d170792b379efa4 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 18 Apr 2022 23:32:53 -0300 Subject: [PATCH 30/67] refactor(cacheservice): refactor updateSchemes and add processScheme function --- src/services/CacheService.ts | 605 +++++++++++++++++------------------ 1 file changed, 286 insertions(+), 319 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 0aa68bbd01..f57a8f9fe1 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -785,319 +785,45 @@ export default class UtilsService { 'allEvents' ); - // Go over all controller events and add update or remove schemes depending on the event - for ( - let controllerEventsIndex = 0; - controllerEventsIndex < controllerEvents.length; - controllerEventsIndex++ - ) { - const controllerEvent = controllerEvents[controllerEventsIndex]; - - const schemeAddress = controllerEvent.returnValues._scheme; - - // Add or update the scheme information, - // register scheme is used to add a scheme or update the parametersHash of an existent one - if (controllerEvent.event === 'RegisterScheme') { - const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); - - console.debug( - 'Register Scheme event for ', - schemeAddress, - schemeTypeData - ); - - let controllerAddress = networkWeb3Contracts.controller._address; - let schemeName = schemeTypeData.name; - let maxSecondsForExecution = bnum(0); - let maxRepPercentageChange = bnum(0); - let schemeType = schemeTypeData.type; - - let callsToExecute = [ - [ - controllerAddress, - 'getSchemePermissions(address,address)', - [schemeAddress, networkWeb3Contracts.avatar._address], - ['bytes4'], - ], - [ - controllerAddress, - 'getSchemeParameters(address,address)', - [schemeAddress, networkWeb3Contracts.avatar._address], - ['bytes32'], - ], - ]; - - if (schemeType === 'WalletScheme') { - const walletSchemeType = ( - await executeMulticall(networkWeb3Contracts.multicall, [ - [schemeAddress, 'SCHEME_TYPE()', [], ['string']], - ]) - ).decodedReturnData[0][0]; - - schemeType = walletSchemeType; - if (schemeType == 'Wallet Scheme v1') - schemeType = 'Wallet Scheme v1.0'; - - switch (schemeType) { - case 'Wallet Scheme v1.0': - callsToExecute.push([ - schemeAddress, - 'votingMachine()', - [], - ['address'], - ]); - callsToExecute.push([ - schemeAddress, - 'controllerAddress()', - [], - ['address'], - ]); - callsToExecute.push([ - schemeAddress, - 'schemeName()', - [], - ['string'], - ]); - callsToExecute.push([ - schemeAddress, - 'maxSecondsForExecution()', - [], - ['uint256'], - ]); - callsToExecute.push([ - schemeAddress, - 'maxRepPercentageChange()', - [], - ['uint256'], - ]); - break; - default: - callsToExecute.push([ - schemeAddress, - 'votingMachine()', - [], - ['address'], - ]); - callsToExecute.push([ - schemeAddress, - 'doAvatarGenericCalls()', - [], - ['bool'], - ]); - callsToExecute.push([ - schemeAddress, - 'schemeName()', - [], - ['string'], - ]); - callsToExecute.push([ - schemeAddress, - 'maxSecondsForExecution()', - [], - ['uint256'], - ]); - callsToExecute.push([ - schemeAddress, - 'maxRepPercentageChange()', - [], - ['uint256'], - ]); - break; - } - } - - const callsResponse1 = await executeMulticall( - networkWeb3Contracts.multicall, - callsToExecute - ); - callsToExecute = []; - - const permissions = decodePermission( - callsResponse1.decodedReturnData[0][0] - ); - const paramsHash = - schemeTypeData.voteParams || callsResponse1.decodedReturnData[1][0]; - - const votingMachineAddress = - schemeTypeData.votingMachine || - callsResponse1.decodedReturnData[2][0]; - - const votingMachine = - networkWeb3Contracts.votingMachines[votingMachineAddress].contract; - - if (schemeTypeData.type === 'WalletScheme') { - switch (schemeType) { - case 'Wallet Scheme v1.0': - controllerAddress = callsResponse1.decodedReturnData[3][0]; - break; - default: - controllerAddress = callsResponse1.decodedReturnData[3][0] - ? networkWeb3Contracts.controller._address - : ZERO_ADDRESS; - break; - } - schemeName = callsResponse1.decodedReturnData[4][0]; - maxSecondsForExecution = callsResponse1.decodedReturnData[5][0]; - maxRepPercentageChange = callsResponse1.decodedReturnData[6][0]; - } - - // Register the new voting parameters in the voting machine params - const votingParameters = await votingMachine.methods - .parameters(paramsHash) - .call(); - networkCache.votingMachines[votingMachine._address].votingParameters[ - paramsHash - ] = { - queuedVoteRequiredPercentage: - votingParameters.queuedVoteRequiredPercentage, - queuedVotePeriodLimit: votingParameters.queuedVotePeriodLimit, - boostedVotePeriodLimit: votingParameters.boostedVotePeriodLimit, - preBoostedVotePeriodLimit: votingParameters.preBoostedVotePeriodLimit, - thresholdConst: votingParameters.thresholdConst, - limitExponentValue: votingParameters.limitExponentValue, - quietEndingPeriod: votingParameters.quietEndingPeriod, - proposingRepReward: votingParameters.proposingRepReward, - votersReputationLossRatio: votingParameters.votersReputationLossRatio, - minimumDaoBounty: votingParameters.minimumDaoBounty, - daoBountyConst: votingParameters.daoBountyConst, - activationTime: votingParameters.activationTime, - }; - - // If the scheme not exist, register it - if (!networkCache.schemes[schemeAddress]) { - networkCache.schemes[schemeAddress] = { - address: schemeAddress, - registered: true, - controllerAddress, - name: schemeName, - type: schemeType, - votingMachine: votingMachineAddress, - paramsHash: paramsHash, - permissions, - boostedVoteRequiredPercentage: 0, - proposalIds: [], - boostedProposals: 0, - maxSecondsForExecution, - maxRepPercentageChange, - newProposalEvents: [], - }; - } else { - networkCache.schemes[schemeAddress].paramsHash = paramsHash; - networkCache.schemes[schemeAddress].permissions = permissions; - } - - // Mark scheme as not registered but save all previous data - } else if ( - controllerEvent.event === 'UnregisterScheme' && - // This condition is added to skip the first scheme added (that is the dao creator account) - controllerEvent.returnValues._sender !== schemeAddress - ) { - const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); - console.debug('Unregister scheme event', schemeAddress, schemeTypeData); - let callsToExecute = [ - [ - networkCache.schemes[schemeAddress].votingMachine, - 'orgBoostedProposalsCnt(bytes32)', - [ - web3.utils.soliditySha3( - schemeAddress, - networkWeb3Contracts.avatar._address - ), - ], - ['uint256'], - ], - ]; + await batchPromisesOntarget( + controllerEvents + .filter(controllerEvent => { + return ( + (controllerEvent.event === 'UnregisterScheme' || + controllerEvent.event === 'RegisterScheme') && + controllerEvent.returnValues._sender !== + controllerEvent.returnValues._scheme + ); + }) + .map(controllerEvent => { + console.log(controllerEvent); + return this.processScheme( + controllerEvent.returnValues._scheme, + networkCache, + networkContracts, + web3, + controllerEvent.event === 'UnregisterScheme' + ); + }), + networkCache, + 5 + ); - if (isWalletScheme(networkCache.schemes[schemeAddress])) { - callsToExecute.push([ + await batchPromisesOntarget( + Object.keys(networkCache.schemes) + .filter(schemeAddress => { + return networkCache.schemes[schemeAddress].registered; + }) + .map(schemeAddress => { + return this.processScheme( schemeAddress, - 'maxSecondsForExecution()', - [], - ['uint256'], - ]); - } - const callsResponse = await executeMulticall( - networkWeb3Contracts.multicall, - callsToExecute - ); - - const maxSecondsForExecution = isWalletScheme( - networkCache.schemes[schemeAddress] - ) - ? callsResponse.decodedReturnData[2][0] - : 0; - - // Update the scheme values a last time - networkCache.schemes[schemeAddress].boostedProposals = - callsResponse.decodedReturnData[0][0]; - networkCache.schemes[schemeAddress].maxSecondsForExecution = - maxSecondsForExecution; - networkCache.schemes[schemeAddress].registered = false; - } - } - - // Update registered schemes - await Promise.all( - Object.keys(networkCache.schemes).map(async schemeAddress => { - if (networkCache.schemes[schemeAddress].registered) { - let callsToExecute = [ - [ - networkCache.schemes[schemeAddress].votingMachine, - 'orgBoostedProposalsCnt(bytes32)', - [ - web3.utils.soliditySha3( - schemeAddress, - networkWeb3Contracts.avatar._address - ), - ], - ['uint256'], - ], - ]; - - if (isWalletScheme(networkCache.schemes[schemeAddress])) { - callsToExecute.push([ - schemeAddress, - 'maxSecondsForExecution()', - [], - ['uint256'], - ]); - callsToExecute.push([ - networkCache.schemes[schemeAddress].votingMachine, - 'boostedVoteRequiredPercentage(bytes32,bytes32)', - [ - web3.utils.soliditySha3( - schemeAddress, - networkWeb3Contracts.avatar._address - ), - networkCache.schemes[schemeAddress].paramsHash, - ], - ['uint256'], - ]); - } - const callsResponse = await executeMulticall( - networkWeb3Contracts.multicall, - callsToExecute + networkCache, + networkContracts, + web3 ); - - const maxSecondsForExecution = isWalletScheme( - networkCache.schemes[schemeAddress] - ) - ? callsResponse.decodedReturnData[1][0] - : 0; - - const boostedVoteRequiredPercentage = isWalletScheme( - networkCache.schemes[schemeAddress] - ) - ? callsResponse.decodedReturnData[2][0] - : 0; - networkCache.schemes[schemeAddress].boostedProposals = - callsResponse.decodedReturnData[0][0]; - networkCache.schemes[schemeAddress].maxSecondsForExecution = - maxSecondsForExecution; - networkCache.schemes[schemeAddress].boostedVoteRequiredPercentage = - boostedVoteRequiredPercentage; - } - }) + }), + networkCache, + 5 ); return networkCache; @@ -1161,14 +887,16 @@ export default class UtilsService { // Update existent active proposals await batchPromisesOntarget( - //@ts-ignore - Object.keys(networkCache.proposals).map(proposalId => { - if ( - networkCache.proposals[proposalId].stateInVotingMachine > - VotingMachineProposalState.Executed || - networkCache.proposals[proposalId].stateInScheme === - WalletSchemeProposalState.Submitted - ) + Object.keys(networkCache.proposals) + .filter(proposalId => { + return ( + networkCache.proposals[proposalId].stateInVotingMachine > + VotingMachineProposalState.Executed || + networkCache.proposals[proposalId].stateInScheme === + WalletSchemeProposalState.Submitted + ); + }) + .map(proposalId => { return this.processProposal( proposalId, networkCache, @@ -1177,7 +905,7 @@ export default class UtilsService { fromBlock, toBlock ); - }), + }), networkCache, 5 ); @@ -1267,6 +995,245 @@ export default class UtilsService { return proposalTitles; } + // Update all the schemes information + async processScheme( + schemeAddress: string, + networkCache: DaoNetworkCache, + networkContracts: NetworkContracts, + web3: Web3, + removeScheme: boolean = false + ): Promise { + const networkWeb3Contracts = await getContracts(networkContracts, web3); + const isNewScheme = !networkCache.schemes[schemeAddress]; + const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); + console.debug( + 'Processing Scheme', + schemeAddress, + schemeTypeData, + removeScheme + ); + + let controllerAddress = networkWeb3Contracts.controller._address; + let schemeName = schemeTypeData.name; + let maxSecondsForExecution = 0; + let maxRepPercentageChange = 0; + let schemeType = schemeTypeData.type; + const isWalletScheme = schemeType === 'WalletScheme'; + + let callsToExecute = [ + [ + controllerAddress, + 'getSchemePermissions(address,address)', + [schemeAddress, networkWeb3Contracts.avatar._address], + ['bytes4'], + ], + [ + controllerAddress, + 'getSchemeParameters(address,address)', + [schemeAddress, networkWeb3Contracts.avatar._address], + ['bytes32'], + ], + ]; + + if (isNewScheme && isWalletScheme) { + schemeType = ( + await executeMulticall(networkWeb3Contracts.multicall, [ + [schemeAddress, 'SCHEME_TYPE()', [], ['string']], + ]) + ).decodedReturnData[0][0]; + if (schemeType === 'Wallet Scheme v1') schemeType = 'Wallet Scheme v1.0'; + + callsToExecute.push([schemeAddress, 'votingMachine()', [], ['address']]); + callsToExecute.push([schemeAddress, 'schemeName()', [], ['string']]); + callsToExecute.push([ + schemeAddress, + 'maxSecondsForExecution()', + [], + ['uint256'], + ]); + callsToExecute.push([ + schemeAddress, + 'maxRepPercentageChange()', + [], + ['uint256'], + ]); + + switch (schemeType) { + case 'Wallet Scheme v1.0': + callsToExecute.push([ + schemeAddress, + 'controllerAddress()', + [], + ['address'], + ]); + break; + default: + callsToExecute.push([ + schemeAddress, + 'doAvatarGenericCalls()', + [], + ['bool'], + ]); + break; + } + } + + const callsResponse1 = await executeMulticall( + networkWeb3Contracts.multicall, + callsToExecute + ); + + const permissions = decodePermission( + callsResponse1.decodedReturnData[0][0] + ); + const paramsHash = + schemeTypeData.voteParams || callsResponse1.decodedReturnData[1][0]; + + const votingMachineAddress = !isNewScheme + ? networkCache.schemes[schemeAddress].votingMachine + : isWalletScheme + ? callsResponse1.decodedReturnData[2][0] + : schemeTypeData.votingMachine; + + if (isNewScheme && isWalletScheme) { + schemeName = callsResponse1.decodedReturnData[3][0]; + maxSecondsForExecution = callsResponse1.decodedReturnData[4][0]; + maxRepPercentageChange = callsResponse1.decodedReturnData[5][0]; + + switch (schemeType) { + case 'Wallet Scheme v1.0': + controllerAddress = callsResponse1.decodedReturnData[6][0]; + break; + default: + controllerAddress = callsResponse1.decodedReturnData[6][0] + ? networkWeb3Contracts.controller._address + : ZERO_ADDRESS; + break; + } + } + + callsToExecute = [ + [ + votingMachineAddress, + 'orgBoostedProposalsCnt(bytes32)', + [ + web3.utils.soliditySha3( + schemeAddress, + networkWeb3Contracts.avatar._address + ), + ], + ['uint256'], + ], + ]; + + // Register the new voting parameters in the voting machine params + if ( + !networkCache.votingMachines[votingMachineAddress].votingParameters[ + paramsHash + ] + ) { + callsToExecute.push([ + votingMachineAddress, + 'parameters(bytes32)', + [paramsHash], + [ + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'address', + ], + ]); + } + + if (isWalletScheme) { + callsToExecute.push([ + votingMachineAddress, + 'boostedVoteRequiredPercentage(bytes32,bytes32)', + [ + web3.utils.soliditySha3( + schemeAddress, + networkWeb3Contracts.avatar._address + ), + paramsHash, + ], + ['uint256'], + ]); + } + + const callsResponse2 = await executeMulticall( + networkWeb3Contracts.multicall, + callsToExecute + ); + + const boostedProposals = callsResponse2.decodedReturnData[0][0]; + + if ( + !networkCache.votingMachines[votingMachineAddress].votingParameters[ + paramsHash + ] + ) { + networkCache.votingMachines[votingMachineAddress].votingParameters[ + paramsHash + ] = { + queuedVoteRequiredPercentage: callsResponse2.decodedReturnData[1][0], + queuedVotePeriodLimit: callsResponse2.decodedReturnData[1][1], + boostedVotePeriodLimit: callsResponse2.decodedReturnData[1][2], + preBoostedVotePeriodLimit: callsResponse2.decodedReturnData[1][3], + thresholdConst: callsResponse2.decodedReturnData[1][4], + limitExponentValue: callsResponse2.decodedReturnData[1][5], + quietEndingPeriod: callsResponse2.decodedReturnData[1][6], + proposingRepReward: callsResponse2.decodedReturnData[1][7], + votersReputationLossRatio: callsResponse2.decodedReturnData[1][8], + minimumDaoBounty: callsResponse2.decodedReturnData[1][9], + daoBountyConst: callsResponse2.decodedReturnData[1][10], + activationTime: callsResponse2.decodedReturnData[1][11], + }; + } + + const boostedVoteRequiredPercentage = isWalletScheme + ? callsResponse2.decodedReturnData[callsToExecute.length - 1][0] + : 0; + + if (isNewScheme) { + networkCache.schemes[schemeAddress] = { + address: schemeAddress, + registered: true, + controllerAddress, + name: schemeName, + type: schemeType, + votingMachine: votingMachineAddress, + paramsHash: paramsHash, + permissions, + boostedVoteRequiredPercentage, + proposalIds: [], + boostedProposals: boostedProposals, + maxSecondsForExecution: maxSecondsForExecution, + maxRepPercentageChange, + newProposalEvents: [], + }; + } else { + networkCache.schemes[schemeAddress].boostedProposals = boostedProposals; + networkCache.schemes[schemeAddress].maxSecondsForExecution = + maxSecondsForExecution; + networkCache.schemes[schemeAddress].boostedVoteRequiredPercentage = + boostedVoteRequiredPercentage; + networkCache.schemes[schemeAddress].paramsHash = paramsHash; + networkCache.schemes[schemeAddress].permissions = permissions; + networkCache.schemes[schemeAddress].registered = !removeScheme; + } + + return networkCache; + } + async processProposal( proposalId: string, networkCache: DaoNetworkCache, From 1d8c1f3207b42f32c85755293cd28ad08696e0a2 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Tue, 19 Apr 2022 11:34:00 -0300 Subject: [PATCH 31/67] refactor(utils/cache): create utils functions for cache from duplicated code in the app --- src/services/CacheService.ts | 45 +++++++++++++---------------------- src/stores/BlockchainStore.ts | 5 ++-- src/stores/ConfigStore.ts | 33 +++++-------------------- src/utils/cache.ts | 30 +++++++++++++++++++++++ 4 files changed, 56 insertions(+), 57 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index f57a8f9fe1..25b84d970c 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1,5 +1,12 @@ import RootContext from '../contexts'; -import { batchPromisesOntarget, getIPFSFile, NETWORK_NAMES } from '../utils'; +import { + batchPromisesOntarget, + getAppConfig, + getDefaultConfigHashes, + getIPFSFile, + getProposalTitles, + NETWORK_NAMES, +} from '../utils'; import Web3 from 'web3'; import _ from 'lodash'; import { @@ -24,26 +31,6 @@ import { getContracts } from '../contracts'; const Hash = require('ipfs-only-hash'); const jsonSort = require('json-keys-sort'); -const defaultConfigHashes = require('../configs/default.json'); - -const arbitrum = require('../configs/arbitrum/config.json'); -const arbitrumTestnet = require('../configs/arbitrumTestnet/config.json'); -const mainnet = require('../configs/mainnet/config.json'); -const xdai = require('../configs/xdai/config.json'); -const rinkeby = require('../configs/rinkeby/config.json'); -const localhost = require('../configs/localhost/config.json'); - -const proposalTitles = require('../configs/proposalTitles.json'); - -const appConfig: AppConfig = { - arbitrum, - arbitrumTestnet, - mainnet, - xdai, - rinkeby, - localhost, -}; - export default class UtilsService { context: RootContext; @@ -75,7 +62,7 @@ export default class UtilsService { }; }> { const updatedCacheConfig = { - proposalTitles: proposalTitles, + proposalTitles: getProposalTitles(), configHashes: {}, configs: {}, caches: {}, @@ -105,9 +92,9 @@ export default class UtilsService { ); updatedCacheConfig.proposalTitles = Object.assign( updatedCacheConfig.proposalTitles, - await this.getProposalTitlesFromIPFS( + await this.updateProposalTitles( cacheForNetwork.cache, - proposalTitles + getProposalTitles() ) ); } @@ -129,7 +116,7 @@ export default class UtilsService { const networkName = NETWORK_NAMES[chainId]; // Get the network configuration - let networkConfig = appConfig[networkName]; + let networkConfig = getAppConfig()[networkName]; let networkCache: DaoNetworkCache; const emptyCache: DaoNetworkCache = { @@ -161,10 +148,12 @@ export default class UtilsService { networkCache = emptyCache; } else { console.log( - `Getting config file from https://ipfs.io/ipfs/${defaultConfigHashes[networkName]}` + `Getting config file from https://ipfs.io/ipfs/${ + getDefaultConfigHashes()[networkName] + }` ); const networkConfigFileFetch = await getIPFSFile( - defaultConfigHashes[networkName], + getDefaultConfigHashes()[networkName], 5000 ); console.log( @@ -913,7 +902,7 @@ export default class UtilsService { return networkCache; } - async getProposalTitlesFromIPFS( + async updateProposalTitles( networkCache: DaoNetworkCache, proposalTitles: Record ) { diff --git a/src/stores/BlockchainStore.ts b/src/stores/BlockchainStore.ts index dca9e65d60..d8ad31b952 100644 --- a/src/stores/BlockchainStore.ts +++ b/src/stores/BlockchainStore.ts @@ -4,6 +4,7 @@ import { Web3ReactContextInterface } from '@web3-react/core/dist/types'; import { isChainIdSupported } from '../provider/connectors'; import { CacheLoadError } from '../utils/errors'; import { bnum } from 'utils'; +import { getProposalTitles } from '../utils/cache'; const targetCacheVersion = 1; @@ -142,9 +143,9 @@ export default class BlockchainStore { true, `Getting proposal titles form ipfs` ); - const proposalTitles = await cacheService.getProposalTitlesFromIPFS( + const proposalTitles = await cacheService.updateProposalTitles( networkCache, - configStore.getProposalTitlesInBuild() + getProposalTitles() ); Object.keys(networkCache.proposals).map(proposalId => { diff --git a/src/stores/ConfigStore.ts b/src/stores/ConfigStore.ts index 4e1fc74d53..d2ab5c4d2d 100644 --- a/src/stores/ConfigStore.ts +++ b/src/stores/ConfigStore.ts @@ -10,26 +10,7 @@ import { DEFAULT_CHAIN_ID, } from '../utils'; import { ZERO_ADDRESS, ANY_ADDRESS, ANY_FUNC_SIGNATURE } from '../utils'; - -const arbitrum = require('../configs/arbitrum/config.json'); -const arbitrumTestnet = require('../configs/arbitrumTestnet/config.json'); -const mainnet = require('../configs/mainnet/config.json'); -const xdai = require('../configs/xdai/config.json'); -const rinkeby = require('../configs/rinkeby/config.json'); -const localhost = require('../configs/localhost/config.json'); -const proposalTitles = require('../configs/proposalTitles.json'); - -const defaultAppConfigs = { - arbitrum, - arbitrumTestnet, - mainnet, - xdai, - rinkeby, - localhost, -}; - -// Use the same content outside src folder in defaultConfigHashes.json or override -const defaultCacheConfig = require('../configs/default.json'); +import { getDefaultConfigHashes, getAppConfig } from '../utils/cache'; export default class ConfigStore { darkMode: boolean; @@ -51,14 +32,14 @@ export default class ConfigStore { } reset() { - this.networkConfig = defaultAppConfigs[this.getActiveChainName()]; + this.networkConfig = getAppConfig()[this.getActiveChainName()]; this.networkConfigLoaded = false; } async loadNetworkConfig() { const { ensService, ipfsService } = this.context; - this.networkConfig = defaultAppConfigs[this.getActiveChainName()]; + this.networkConfig = getAppConfig()[this.getActiveChainName()]; const isTestingEnv = !window?.location?.href?.includes('dxvote.eth'); if (this.getActiveChainName() !== 'localhost' && !this.networkConfigLoaded) @@ -76,7 +57,7 @@ export default class ConfigStore { ); const configRefs = isTestingEnv - ? defaultCacheConfig + ? getDefaultConfigHashes() : await ipfsService.getContentFromIPFS(metadataHash); const configContentHash = configRefs[this.getActiveChainName()]; @@ -105,10 +86,6 @@ export default class ConfigStore { return this.networkConfig; } - getProposalTitlesInBuild() { - return proposalTitles; - } - getActiveChainName() { return NETWORK_NAMES[ this.context?.providerStore.getActiveWeb3React().chainId || @@ -124,6 +101,7 @@ export default class ConfigStore { } getLocalConfig() { + const defaultAppConfigs = getAppConfig(); const defaultConfig = { etherscan: '', pinata: '', @@ -153,6 +131,7 @@ export default class ConfigStore { } resetLocalConfig() { + const defaultAppConfigs = getAppConfig(); localStorage.setItem( 'dxvote-config', JSON.stringify({ diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 060db54859..a1ee2244b9 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -6,6 +6,36 @@ import { sleep } from './helpers'; const Web3 = require('web3'); const web3 = new Web3(); +const arbitrum = require('../configs/arbitrum/config.json'); +const arbitrumTestnet = require('../configs/arbitrumTestnet/config.json'); +const mainnet = require('../configs/mainnet/config.json'); +const xdai = require('../configs/xdai/config.json'); +const rinkeby = require('../configs/rinkeby/config.json'); +const localhost = require('../configs/localhost/config.json'); + +const proposalTitles = require('../configs/proposalTitles.json'); + +const defaultConfigHashes = require('../configs/default.json'); + +export const getDefaultConfigHashes = (): Record => { + return defaultConfigHashes; +}; + +export const getProposalTitles = (): Record => { + return proposalTitles; +}; + +export const getAppConfig = (): AppConfig => { + return { + arbitrum, + arbitrumTestnet, + mainnet, + xdai, + rinkeby, + localhost, + }; +}; + export const getEvents = async function ( web3, contract, From 71fc691c0ebb2b575b267bc9a723b472c0cc8c28 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Tue, 19 Apr 2022 11:35:59 -0300 Subject: [PATCH 32/67] refactor(cacheservice): rmeove not needed context parameter --- src/services/CacheService.ts | 4 +--- src/stores/BlockchainStore.ts | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 25b84d970c..c518083d7e 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -179,7 +179,6 @@ export default class UtilsService { networkName ); networkCache = await this.getUpdatedCache( - this.context, networkCache, networkConfig.contracts, toBlock, @@ -204,13 +203,12 @@ export default class UtilsService { } async getUpdatedCache( - context: RootContext, networkCache: DaoNetworkCache, networkContracts: NetworkContracts, toBlock: number, web3: Web3 ): Promise { - const notificationStore = context.notificationStore; + const notificationStore = this.context.notificationStore; const fromBlock = networkCache.blockNumber + 1; console.debug(`[CACHE UPDATE] from ${fromBlock} to ${toBlock}`); const networkWeb3Contracts = await getContracts(networkContracts, web3); diff --git a/src/stores/BlockchainStore.ts b/src/stores/BlockchainStore.ts index d8ad31b952..34678008b1 100644 --- a/src/stores/BlockchainStore.ts +++ b/src/stores/BlockchainStore.ts @@ -132,7 +132,6 @@ export default class BlockchainStore { const networkContracts = configStore.getNetworkContracts(); networkCache = await cacheService.getUpdatedCache( - this.context, networkCache, networkContracts, toBlock, From c00eaefc892b21000c4b9f1a9ed7fe03e19e9551 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Tue, 19 Apr 2022 11:37:08 -0300 Subject: [PATCH 33/67] chore(cacheservice): remove commented code --- src/services/CacheService.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index c518083d7e..257e3a8ca4 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -274,13 +274,6 @@ export default class UtilsService { console.log('Total Proposals', Object.keys(networkCache.proposals).length); - // Compare proposals data - // Object.keys(networkCache.proposals).map((proposalId) => { - // const mutableData = getProposalMutableData(networkCache, proposalId); - // const cacheData = networkCache.proposals[proposalId]; - // console.debug(proposalId, mutableData, cacheData); - // }) - // Sort cache data, so the IPFS hash is consistent Object.keys(networkCache.schemes).forEach(schemeId => { networkCache.schemes[schemeId].proposalIds.sort(); From 1a19647ff237516cc6aec495317d12a8d04d3fe5 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 20 Apr 2022 09:16:07 -0300 Subject: [PATCH 34/67] feat(utils): add retryPromise utils function --- src/services/CacheService.ts | 6 +++--- src/utils/helpers.ts | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 257e3a8ca4..0c408e7855 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -6,6 +6,7 @@ import { getIPFSFile, getProposalTitles, NETWORK_NAMES, + retryPromise, } from '../utils'; import Web3 from 'web3'; import _ from 'lodash'; @@ -159,9 +160,8 @@ export default class UtilsService { console.log( `Getting cache file from https://ipfs.io/ipfs/${networkConfigFileFetch.data.cache.ipfsHash}` ); - const networkCacheFetch = await getIPFSFile( - networkConfigFileFetch.data.cache.ipfsHash, - 60000 + const networkCacheFetch = await retryPromise( + getIPFSFile(networkConfigFileFetch.data.cache.ipfsHash, 60000) ); networkCache = networkCacheFetch.data; } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 1c857b62b3..bef93df48f 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -87,3 +87,20 @@ export const appendEthAPIKey = ( export function escapeRegExp(string: string): string { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } + +export async function retryPromise( + promise: Promise, + timeToWait: number = 0 +): Promise { + let toReturn; + while (!toReturn) { + try { + toReturn = await promise; + if (!toReturn) await sleep(timeToWait); + } catch (error) { + console.warn(error); + await sleep(timeToWait); + } + } + return toReturn; +} From eb2448a62b6cf0acaee297cac8f7d31ec8c7b26f Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 20 Apr 2022 09:23:47 -0300 Subject: [PATCH 35/67] feat(utils): add sortNetworkCache function --- src/services/CacheService.ts | 19 ++----------------- src/utils/cache.ts | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 0c408e7855..0eb2bf220c 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -22,6 +22,7 @@ import { getEvents, getRawEvents, executeMulticall, + sortNetworkCache, descriptionHashToIPFSHash, ipfsHashToDescriptionHash, getSchemeConfig, @@ -274,23 +275,7 @@ export default class UtilsService { console.log('Total Proposals', Object.keys(networkCache.proposals).length); - // Sort cache data, so the IPFS hash is consistent - Object.keys(networkCache.schemes).forEach(schemeId => { - networkCache.schemes[schemeId].proposalIds.sort(); - networkCache.schemes[schemeId].newProposalEvents.sort((a, b) => - a.proposalId.localeCompare(b.proposalId) - ); - }); - networkCache.proposals = Object.keys(networkCache.proposals) - .sort() - .reduce((obj, key) => { - obj[key] = networkCache.proposals[key]; - return obj; - }, {}); - networkCache.ipfsHashes = _.uniqBy(networkCache.ipfsHashes, 'name'); - networkCache.ipfsHashes.sort((a, b) => a.name.localeCompare(b.name)); - - return networkCache; + return sortNetworkCache(networkCache); } // Get all Mint and Burn reputation events to calculate rep by time off chain diff --git a/src/utils/cache.ts b/src/utils/cache.ts index a1ee2244b9..5e25102a54 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -36,6 +36,28 @@ export const getAppConfig = (): AppConfig => { }; }; +// Sort cache data, so the IPFS hash is consistent +export const sortNetworkCache = ( + networkCache: DaoNetworkCache +): DaoNetworkCache => { + Object.keys(networkCache.schemes).forEach(schemeId => { + networkCache.schemes[schemeId].proposalIds.sort(); + networkCache.schemes[schemeId].newProposalEvents.sort((a, b) => + a.proposalId.localeCompare(b.proposalId) + ); + }); + networkCache.proposals = Object.keys(networkCache.proposals) + .sort() + .reduce((obj, key) => { + obj[key] = networkCache.proposals[key]; + return obj; + }, {}); + networkCache.ipfsHashes = _.uniqBy(networkCache.ipfsHashes, 'name'); + networkCache.ipfsHashes.sort((a, b) => a.name.localeCompare(b.name)); + + return networkCache; +}; + export const getEvents = async function ( web3, contract, From 192fb7a52bdb4179e70a66aa8ffc717e148df4aa Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 20 Apr 2022 09:27:50 -0300 Subject: [PATCH 36/67] refactor(cacheservice): add comments and process new proposals in processScheme func --- src/services/CacheService.ts | 172 ++++++++++++++++++++--------------- 1 file changed, 98 insertions(+), 74 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 0eb2bf220c..0e2296baf1 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -69,6 +69,7 @@ export default class UtilsService { configs: {}, caches: {}, }; + // Update the cache and config for each network for (let i = 0; i < Object.keys(networksConfig).length; i++) { const networkId = Number(Object.keys(networksConfig)[i]); @@ -139,9 +140,12 @@ export default class UtilsService { }; // Set network cache and config objects + + // If network is localhost we use an empty cache to force the complete cache generation in each load if (networkName === 'localhost') { networkCache = emptyCache; } else { + // If the cache is not reset, we load the cache from the local storage if (resetCache) { networkConfig.cache.toBlock = networkConfig.cache.fromBlock; networkConfig.cache.ipfsHash = ''; @@ -149,6 +153,10 @@ export default class UtilsService { emptyCache.blockNumber = networkConfig.cache.fromBlock; networkCache = emptyCache; } else { + this.context.notificationStore.setGlobalLoading( + true, + `Getting configuration file from IPFS` + ); console.log( `Getting config file from https://ipfs.io/ipfs/${ getDefaultConfigHashes()[networkName] @@ -158,6 +166,10 @@ export default class UtilsService { getDefaultConfigHashes()[networkName], 5000 ); + this.context.notificationStore.setGlobalLoading( + true, + `Getting cache file from IPFS` + ); console.log( `Getting cache file from https://ipfs.io/ipfs/${networkConfigFileFetch.data.cache.ipfsHash}` ); @@ -168,7 +180,7 @@ export default class UtilsService { } } - // Set block range for the script to run, if cache to block is set that value is used, if not we use last block + // Update the cache only if the toBlock is higher than the current networkCache block if (Number(networkCache.blockNumber) + 1 < toBlock) { // The cache file is updated with the data that had before plus new data in the network cache file console.debug( @@ -187,13 +199,15 @@ export default class UtilsService { ); } - // Write network cache file + // Sort json obj and parse it to string networkCache = await jsonSort.sortAsync(networkCache, true); const networkCacheString = JSON.stringify(networkCache, null, 2); - // Update appConfig file with the latest network config + // Update appConfig obj with the latest network config and networkCache hash networkConfig.cache.toBlock = toBlock; networkConfig.cache.ipfsHash = await Hash.of(networkCacheString); + + // Sort config obj and parse it to string networkConfig = await jsonSort.sortAsync(networkConfig, true); return { @@ -209,16 +223,23 @@ export default class UtilsService { toBlock: number, web3: Web3 ): Promise { - const notificationStore = this.context.notificationStore; const fromBlock = networkCache.blockNumber + 1; - console.debug(`[CACHE UPDATE] from ${fromBlock} to ${toBlock}`); const networkWeb3Contracts = await getContracts(networkContracts, web3); - notificationStore.setGlobalLoading( + console.debug(`[CACHE UPDATE] from ${fromBlock} to ${toBlock}`); + + this.context.notificationStore.setGlobalLoading( true, `Collecting reputation and governance events in blocks ${fromBlock} - ${toBlock}` ); + // The first promise round: + // - Reputation Events + // - Voting Machine Events + // - Permission Registry + // - Vesting Contracts + // - Update schemes + networkCache = await batchPromisesOntarget( [ this.updateReputationEvents( @@ -233,18 +254,6 @@ export default class UtilsService { toBlock, web3 ), - this.updateSchemes(networkCache, networkContracts, toBlock, web3), - ], - networkCache - ); - - notificationStore.setGlobalLoading( - true, - `Updating scheme data in blocks ${fromBlock} - ${toBlock}` - ); - - networkCache = await batchPromisesOntarget( - [ this.updatePermissionRegistry( networkCache, networkContracts, @@ -257,15 +266,17 @@ export default class UtilsService { toBlock, web3 ), + this.updateSchemes(networkCache, networkContracts, toBlock, web3), ], networkCache ); - notificationStore.setGlobalLoading( + this.context.notificationStore.setGlobalLoading( true, `Collecting proposals in blocks ${fromBlock} - ${toBlock}` ); + // The second promise round is just to update the proposals that needs the schemes udpated. networkCache = await batchPromisesOntarget( [this.updateProposals(networkCache, networkContracts, toBlock, web3)], networkCache @@ -278,7 +289,7 @@ export default class UtilsService { return sortNetworkCache(networkCache); } - // Get all Mint and Burn reputation events to calculate rep by time off chain + // Get all Mint and Burn reputation events async updateReputationEvents( networkCache: DaoNetworkCache, reputation: any, @@ -750,6 +761,7 @@ export default class UtilsService { 'allEvents' ); + // Process all the schemes that changed their registered state based on the events await batchPromisesOntarget( controllerEvents .filter(controllerEvent => { @@ -761,12 +773,12 @@ export default class UtilsService { ); }) .map(controllerEvent => { - console.log(controllerEvent); return this.processScheme( controllerEvent.returnValues._scheme, networkCache, networkContracts, web3, + toBlock, controllerEvent.event === 'UnregisterScheme' ); }), @@ -774,6 +786,7 @@ export default class UtilsService { 5 ); + // Process all the registered schemes await batchPromisesOntarget( Object.keys(networkCache.schemes) .filter(schemeAddress => { @@ -784,7 +797,8 @@ export default class UtilsService { schemeAddress, networkCache, networkContracts, - web3 + web3, + toBlock ); }), networkCache, @@ -794,71 +808,33 @@ export default class UtilsService { return networkCache; } - // Update all the proposals information + // Update the proposals information async updateProposals( networkCache: DaoNetworkCache, networkContracts: NetworkContracts, toBlock: number, web3: Web3 ): Promise { - const networkWeb3Contracts = await getContracts(networkContracts, web3); - const avatarAddress = networkWeb3Contracts.avatar._address; - const avatarAddressEncoded = web3.eth.abi.encodeParameter( - 'address', - avatarAddress - ); const fromBlock = networkCache.blockNumber + 1; - // Get new proposals - // TO DO: Get only proposals from registered schemes, change registered to block number of unregisterScheme - await Promise.all( - Object.keys(networkCache.schemes).map(async schemeAddress => { - const schemeTypeData = getSchemeConfig(networkContracts, schemeAddress); - let schemeEvents = []; - for (let i = 0; i < schemeTypeData.newProposalTopics.length; i++) { - schemeEvents = schemeEvents.concat( - await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - schemeTypeData.newProposalTopics[i] - ) - ); - } - - await batchPromisesOntarget( - schemeEvents.map(schemeEvent => { - const proposalId: string = - schemeEvent.topics[1] === avatarAddressEncoded - ? schemeEvent.topics[2] - : schemeEvent.topics[1]; - - return this.processProposal( - proposalId, - networkCache, - networkContracts, - web3, - fromBlock, - toBlock, - schemeEvent - ); - }), - networkCache, - 50 - ); - }) - ); - // Update existent active proposals await batchPromisesOntarget( Object.keys(networkCache.proposals) .filter(proposalId => { return ( - networkCache.proposals[proposalId].stateInVotingMachine > + // Only update proposals for registered schemes + networkCache.schemes[networkCache.proposals[proposalId].scheme] + .registered && + // This condition check that the proposal already existed in the current block range. + // If not, it means that the proposal was created in the current block range when processing the scheme, + // So there is no need to process it again. + networkCache.proposals[proposalId].creationEvent.blockNumber < + fromBlock && + // This condition check that the proposal is active + (networkCache.proposals[proposalId].stateInVotingMachine > VotingMachineProposalState.Executed || - networkCache.proposals[proposalId].stateInScheme === - WalletSchemeProposalState.Submitted + networkCache.proposals[proposalId].stateInScheme === + WalletSchemeProposalState.Submitted) ); }) .map(proposalId => { @@ -960,12 +936,16 @@ export default class UtilsService { return proposalTitles; } - // Update all the schemes information + // Process a scheme in the networkCache + // If the scheme does not exist in the cache it gets the immutable + mutable data + // The scheme already exists it gets the mutable data only + // At the end it process all the new proposals added in the scheme to the toBlock async processScheme( schemeAddress: string, networkCache: DaoNetworkCache, networkContracts: NetworkContracts, web3: Web3, + toBlock: number, removeScheme: boolean = false ): Promise { const networkWeb3Contracts = await getContracts(networkContracts, web3); @@ -1196,9 +1176,53 @@ export default class UtilsService { networkCache.schemes[schemeAddress].registered = !removeScheme; } + // Get the new proposals submitted in the scheme and process it + const avatarAddressEncoded = web3.eth.abi.encodeParameter( + 'address', + networkCache.address + ); + const fromBlock = networkCache.blockNumber + 1; + + let schemeEvents = []; + for (let i = 0; i < schemeTypeData.newProposalTopics.length; i++) { + schemeEvents = schemeEvents.concat( + await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + schemeTypeData.newProposalTopics[i] + ) + ); + } + + await batchPromisesOntarget( + schemeEvents.map(schemeEvent => { + const proposalId: string = + schemeEvent.topics[1] === avatarAddressEncoded + ? schemeEvent.topics[2] + : schemeEvent.topics[1]; + + return this.processProposal( + proposalId, + networkCache, + networkContracts, + web3, + fromBlock, + toBlock, + schemeEvent + ); + }), + networkCache, + 50 + ); + return networkCache; } + // Process a proposal in the networkCache + // If the proposal does not exist in the cache it gets the immutable + mutable data + // The proposal already exists it gets the mutable data only async processProposal( proposalId: string, networkCache: DaoNetworkCache, From d3d5185450aa95c76abfa103909aa180d7d03835 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Wed, 20 Apr 2022 14:47:14 +0200 Subject: [PATCH 37/67] chore: removed logs and added hooks --- .../REPMint/REPMintSummary.tsx | 28 +++---------------- src/hooks/Guilds/guild/useTokenData.ts | 15 ++++++++++ src/hooks/Guilds/guild/useTotalSupply.ts | 23 +++++++++++++++ 3 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 src/hooks/Guilds/guild/useTokenData.ts create mode 100644 src/hooks/Guilds/guild/useTotalSupply.ts diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index 4698c7cbd2..5396e66909 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -1,36 +1,16 @@ import Avatar from 'components/Guilds/Avatar'; -import { BigNumber } from 'ethers'; import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; -import { useMemo } from 'react'; -import { useParams } from 'react-router-dom'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; import { Segment } from '../common/infoLine'; import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; - -interface REPMintState { - toAddress: string; - amount: BigNumber; -} - +import { useTotalSupply } from 'hooks/Guilds/guild/useTotalSupply'; +import { useTokenData } from 'hooks/Guilds/guild/useTokenData'; const REPMintSummary: React.FC = ({ decodedCall }) => { - const { guild_id: guildId } = - useParams<{ chain_name?: string; guild_id?: string }>(); - const { data } = useGuildConfig(guildId); - const { data: tokenData } = useERC20Info(data?.token); - console.log({ tokenData }); - console.log(tokenData.symbol); - const parsedData = useMemo(() => { - if (!decodedCall) return null; - return { - toAddress: decodedCall.args.to, - amount: decodedCall.args.amount, - }; - }, [decodedCall]); + const { parsedData } = useTotalSupply({ decodedCall }) + const { tokenData } = useTokenData() const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 18, 3); diff --git a/src/hooks/Guilds/guild/useTokenData.ts b/src/hooks/Guilds/guild/useTokenData.ts new file mode 100644 index 0000000000..ee35d3d126 --- /dev/null +++ b/src/hooks/Guilds/guild/useTokenData.ts @@ -0,0 +1,15 @@ +import { useParams } from "react-router-dom"; +import { useGuildConfig } from "../ether-swr/guild/useGuildConfig"; +import { useERC20Info } from "../ether-swr/erc20/useERC20Info"; + +export const useTokenData = () => { + const { guild_id: guildId } = + useParams<{ chain_name?: string; guild_id?: string }>(); + const { data } = useGuildConfig(guildId); + const { data: tokenData } = useERC20Info(data?.token); + + return { + tokenData + } +} + diff --git a/src/hooks/Guilds/guild/useTotalSupply.ts b/src/hooks/Guilds/guild/useTotalSupply.ts new file mode 100644 index 0000000000..f08baf6adf --- /dev/null +++ b/src/hooks/Guilds/guild/useTotalSupply.ts @@ -0,0 +1,23 @@ + +import { BigNumber } from 'ethers'; +import { useMemo } from "react"; + +interface REPMintState { + toAddress: string; + amount: BigNumber; + } + +export const useTotalSupply = ({ decodedCall }) => { + + const parsedData = useMemo(() => { + if (!decodedCall) return null; + return { + toAddress: decodedCall.args.to, + amount: decodedCall.args.amount, + }; + }, [decodedCall]); + + return { + parsedData, + } +} From e9b97cdc4c0c7dcfa296bebf5b60a0fce9b03f86 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Wed, 20 Apr 2022 14:48:16 +0200 Subject: [PATCH 38/67] chore: format --- .../REPMint/REPMintSummary.tsx | 5 +-- src/hooks/Guilds/guild/useTokenData.ts | 29 ++++++------ src/hooks/Guilds/guild/useTotalSupply.ts | 44 +++++++++---------- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index 5396e66909..1b4418477e 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -8,9 +8,8 @@ import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; import { useTotalSupply } from 'hooks/Guilds/guild/useTotalSupply'; import { useTokenData } from 'hooks/Guilds/guild/useTokenData'; const REPMintSummary: React.FC = ({ decodedCall }) => { - - const { parsedData } = useTotalSupply({ decodedCall }) - const { tokenData } = useTokenData() + const { parsedData } = useTotalSupply({ decodedCall }); + const { tokenData } = useTokenData(); const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 18, 3); diff --git a/src/hooks/Guilds/guild/useTokenData.ts b/src/hooks/Guilds/guild/useTokenData.ts index ee35d3d126..72d012edaf 100644 --- a/src/hooks/Guilds/guild/useTokenData.ts +++ b/src/hooks/Guilds/guild/useTokenData.ts @@ -1,15 +1,14 @@ -import { useParams } from "react-router-dom"; -import { useGuildConfig } from "../ether-swr/guild/useGuildConfig"; -import { useERC20Info } from "../ether-swr/erc20/useERC20Info"; - -export const useTokenData = () => { - const { guild_id: guildId } = - useParams<{ chain_name?: string; guild_id?: string }>(); - const { data } = useGuildConfig(guildId); - const { data: tokenData } = useERC20Info(data?.token); - - return { - tokenData - } -} - +import { useParams } from 'react-router-dom'; +import { useGuildConfig } from '../ether-swr/guild/useGuildConfig'; +import { useERC20Info } from '../ether-swr/erc20/useERC20Info'; + +export const useTokenData = () => { + const { guild_id: guildId } = + useParams<{ chain_name?: string; guild_id?: string }>(); + const { data } = useGuildConfig(guildId); + const { data: tokenData } = useERC20Info(data?.token); + + return { + tokenData, + }; +}; diff --git a/src/hooks/Guilds/guild/useTotalSupply.ts b/src/hooks/Guilds/guild/useTotalSupply.ts index f08baf6adf..94af8143c6 100644 --- a/src/hooks/Guilds/guild/useTotalSupply.ts +++ b/src/hooks/Guilds/guild/useTotalSupply.ts @@ -1,23 +1,21 @@ - -import { BigNumber } from 'ethers'; -import { useMemo } from "react"; - -interface REPMintState { - toAddress: string; - amount: BigNumber; - } - -export const useTotalSupply = ({ decodedCall }) => { - - const parsedData = useMemo(() => { - if (!decodedCall) return null; - return { - toAddress: decodedCall.args.to, - amount: decodedCall.args.amount, - }; - }, [decodedCall]); - - return { - parsedData, - } -} +import { BigNumber } from 'ethers'; +import { useMemo } from 'react'; + +interface REPMintState { + toAddress: string; + amount: BigNumber; +} + +export const useTotalSupply = ({ decodedCall }) => { + const parsedData = useMemo(() => { + if (!decodedCall) return null; + return { + toAddress: decodedCall.args.to, + amount: decodedCall.args.amount, + }; + }, [decodedCall]); + + return { + parsedData, + }; +}; From 29f3ea35925a45543714131843ac6ff318f6d4c4 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Wed, 20 Apr 2022 14:58:00 +0200 Subject: [PATCH 39/67] chore: removed code --- .../REPMint/REPMintEditor.tsx | 29 +++++-------------- .../REPMint/REPMintInfoLine.tsx | 28 ++++-------------- .../REPMint/REPMintSummary.tsx | 1 + 3 files changed, 13 insertions(+), 45 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx index 179193d7b5..be79f80f0a 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -1,9 +1,9 @@ import styled from 'styled-components'; import { Input } from 'components/Guilds/common/Form/Input'; import Avatar from 'components/Guilds/Avatar'; -import { useEffect, useMemo } from 'react'; +import { useEffect } from 'react'; import { ActionEditorProps } from '..'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; import { Box } from 'components/Guilds/common/Layout'; import { shortenAddress, MAINNET_ID } from 'utils'; @@ -12,10 +12,9 @@ import { ReactComponent as Info } from '../../../../../assets/images/info.svg'; import StyledIcon from 'components/Guilds/common/SVG'; import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; import { useState } from 'react'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; -import { useParams } from 'react-router-dom'; import NumericalInput from 'components/Guilds/common/Form/NumericalInput'; +import { useTotalSupply } from 'hooks/Guilds/guild/useTotalSupply'; +import { useTokenData } from 'hooks/Guilds/guild/useTokenData'; const Control = styled(Box)` display: flex; @@ -50,28 +49,14 @@ const RepMintInput = styled(NumericalInput)` } `; -interface REPMintState { - toAddress: string; - amount: BigNumber; -} - const Mint: React.FC = ({ decodedCall, updateCall }) => { // parse transfer state from calls const [repPercent, setRepPercent] = useState(0); const [repAmount, setRepAmount] = useState(0); - const { guild_id: guildId } = - useParams<{ chain_name?: string; guild_id?: string }>(); - const { data } = useGuildConfig(guildId); - const { data: tokenData } = useERC20Info(data?.token); - const totalSupply = useBigNumberToNumber(tokenData?.totalSupply, 18); + const { parsedData } = useTotalSupply({ decodedCall }); + const { tokenData } = useTokenData(); - const parsedData = useMemo(() => { - if (!decodedCall) return null; - return { - toAddress: decodedCall.args.to, - amount: decodedCall.args.amount, - }; - }, [decodedCall]); + const totalSupply = useBigNumberToNumber(tokenData?.totalSupply, 18); const { imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx index 1ead7cc7c3..1f460d2ab4 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintInfoLine.tsx @@ -1,6 +1,5 @@ import Avatar from 'components/Guilds/Avatar'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; -import { useMemo } from 'react'; import { FiArrowRight } from 'react-icons/fi'; import { MAINNET_ID, shortenAddress } from 'utils'; import { ActionViewProps } from '..'; @@ -8,37 +7,20 @@ import { Segment } from '../common/infoLine'; import { ReactComponent as Mint } from '../../../../../assets/images/mint.svg'; import StyledIcon from 'components/Guilds/common/SVG'; import styled from 'styled-components'; -import { BigNumber } from 'ethers'; import useBigNumberToNumber from 'hooks/Guilds/conversions/useBigNumberToNumber'; -import { useERC20Info } from 'hooks/Guilds/ether-swr/erc20/useERC20Info'; -import { useGuildConfig } from 'hooks/Guilds/ether-swr/guild/useGuildConfig'; -import { useParams } from 'react-router-dom'; +import { useTotalSupply } from 'hooks/Guilds/guild/useTotalSupply'; +import { useTokenData } from 'hooks/Guilds/guild/useTokenData'; const StyledMintIcon = styled(StyledIcon)` margin: 0; `; -interface REPMintState { - guildAddress: string; - toAddress: string; - amount: BigNumber; -} - const REPMintInfoLine: React.FC = ({ decodedCall }) => { - const { guild_id: guildId } = - useParams<{ chain_name?: string; guild_id?: string }>(); - const { data } = useGuildConfig(guildId); - const { data: tokenData } = useERC20Info(data?.token); + const { parsedData } = useTotalSupply({ decodedCall }); + const { tokenData } = useTokenData(); + const totalSupply = useBigNumberToNumber(tokenData?.totalSupply, 18); - const parsedData = useMemo(() => { - if (!decodedCall) return null; - return { - guildAddress: decodedCall.to, - toAddress: decodedCall.args.to, - amount: decodedCall.args.amount, - }; - }, [decodedCall]); const { ensName, imageUrl } = useENSAvatar(parsedData?.toAddress, MAINNET_ID); const roundedRepAmount = useBigNumberToNumber(parsedData?.amount, 16, 3); diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx index 1b4418477e..90bd25a6ed 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintSummary.tsx @@ -7,6 +7,7 @@ import { Segment } from '../common/infoLine'; import { DetailCell, DetailHeader, DetailRow } from '../common/summary'; import { useTotalSupply } from 'hooks/Guilds/guild/useTotalSupply'; import { useTokenData } from 'hooks/Guilds/guild/useTokenData'; + const REPMintSummary: React.FC = ({ decodedCall }) => { const { parsedData } = useTotalSupply({ decodedCall }); const { tokenData } = useTokenData(); From b00fee51de26a1061d2a0cfa807da651f231a4db Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 20 Apr 2022 10:06:35 -0300 Subject: [PATCH 40/67] docs(cacheservice): add comments --- src/services/CacheService.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 0e2296baf1..8588addde0 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1245,6 +1245,7 @@ export default class UtilsService { avatarAddress ); + // These first calls target mainly the voting machine where most of the proposal information is mutable let callsToExecute: any[] = [ [ networkCache.schemes[schemeAddress].votingMachine, @@ -1303,6 +1304,7 @@ export default class UtilsService { ], ]; + // The next calls added target ContributionReward and WalletScheme to get immutable and mutable data if (schemeTypeData.type === 'ContributionReward') { callsToExecute.push([ schemeAddress, @@ -1380,12 +1382,12 @@ export default class UtilsService { } else if (isWalletScheme(schemeOfProposal)) { schemeProposalInfo.state = callsResponse.decodedReturnData[6].state; } else { + // Here we get the events triggered in the GenericMulticall to get their final state + // When the proposal is executed by the voting machine we get if the proposal was rejected + // If the proposal was executed by teh voting machine and not rejected we get the ProposalEnded + // to know if it was executed by the WalletScheme if (schemeOfProposal.type === 'GenericMulticall') { - // event ProposalExecutedByVotingMachine( - // address indexed _avatar, - // bytes32 indexed _proposalId, - // int256 _param - // ); + // event ProposalExecutedByVotingMachine(address indexed _avatar,bytes32 indexed _proposalId,int256 _param) const votingMachineExecutionEvent = schemeProposalInfo.state === WalletSchemeProposalState.Submitted ? await getRawEvents( @@ -1428,6 +1430,8 @@ export default class UtilsService { schemeProposalInfo.state = WalletSchemeProposalState.ExecutionSucceded; } + + // If any of the values of the redeemPeriods of the contribution reward is higher than zero it means that it executed the reward } else if (schemeOfProposal.type === 'ContributionReward') { if ( callsResponse.decodedReturnData[6][0] > 0 || @@ -1449,6 +1453,7 @@ export default class UtilsService { } // If the proposal is processed with a creation event it means that it has to be added to the cache + // We will get the immutable data stored on the contracts by decoding the creation event logs if (newProposal) { if (creationEvent && !isWalletScheme(schemeOfProposal)) { const transactionReceipt = await web3.eth.getTransactionReceipt( @@ -1498,6 +1503,8 @@ export default class UtilsService { } } + // Try to decode as much as we can from the creation event, decoding creation logs. + // Depending the type of the scheme we have to decode different data and parse it to decoded calls if (schemeTypeData.type === 'SchemeRegistrar') { schemeProposalInfo.to = [schemeTypeData.contractToCall]; schemeProposalInfo.value = [0]; @@ -1713,7 +1720,7 @@ export default class UtilsService { } } - // Register the new voting parameters in the voting machine params + // Register the new voting parameters in the voting machine params if they dont exist in the cache if ( !networkCache.votingMachines[ networkCache.schemes[schemeAddress].votingMachine @@ -1829,6 +1836,8 @@ export default class UtilsService { name: proposalId, }); } + + // Is the proposal is not new we only assign the values that are mutable } else { networkCache.proposals[proposalId].stateInScheme = Number( schemeProposalInfo.state @@ -1869,6 +1878,7 @@ export default class UtilsService { callsResponse.decodedReturnData[3][3] ); } + return networkCache; } } From a80cb236e3459e79e381f7f43c5b308bf5ee2e92 Mon Sep 17 00:00:00 2001 From: Kenny Chung Date: Wed, 20 Apr 2022 15:06:59 +0200 Subject: [PATCH 41/67] chore: replace div with Fragment --- .../SupportedActions/REPMint/REPMintEditor.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx index be79f80f0a..2c9bfb2241 100644 --- a/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx +++ b/src/components/Guilds/ActionsBuilder/SupportedActions/REPMint/REPMintEditor.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; import { Input } from 'components/Guilds/common/Form/Input'; import Avatar from 'components/Guilds/Avatar'; -import { useEffect } from 'react'; +import React, { useEffect } from 'react'; import { ActionEditorProps } from '..'; import { ethers } from 'ethers'; import useENSAvatar from 'hooks/Guilds/ether-swr/ens/useENSAvatar'; @@ -84,7 +84,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { } }; return ( -
+ Recipient @@ -128,7 +128,7 @@ const Mint: React.FC = ({ decodedCall, updateCall }) => { -
+ ); }; From c3742f3c2be40e1147fbae1163209a1c57212975 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 20 Apr 2022 10:18:37 -0300 Subject: [PATCH 42/67] refactor(cacheservice): forEach for map and genericMulticall final event decode --- src/services/CacheService.ts | 69 ++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 8588addde0..dbc3fe8970 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -307,7 +307,7 @@ export default class UtilsService { 'allEvents' ); - reputationEvents.map(reputationEvent => { + reputationEvents.forEach(reputationEvent => { switch (reputationEvent.event) { case 'Mint': networkCache.reputation.events.push({ @@ -412,7 +412,7 @@ export default class UtilsService { const avatarAddress = web3.utils.toChecksumAddress(networkContracts.avatar); const votingMachineEventsInCache = networkCache.votingMachines[votingMachine._address].events; - newVotingMachineEvents.map(votingMachineEvent => { + newVotingMachineEvents.forEach(votingMachineEvent => { const proposalCreated = votingMachineEventsInCache.newProposal.findIndex( newProposalEvent => @@ -607,7 +607,7 @@ export default class UtilsService { 'allEvents' ); - permissionRegistryEvents.map(permissionRegistryEvent => { + permissionRegistryEvents.forEach(permissionRegistryEvent => { const eventValues = permissionRegistryEvent.returnValues; if (!networkCache.callPermissions[eventValues.asset]) @@ -1352,8 +1352,6 @@ export default class UtilsService { callsToExecute ); - const positiveVotes = callsResponse.decodedReturnData[1][0]; - const negativeVotes = callsResponse.decodedReturnData[2][0]; const proposalTimes = callsResponse.decodedReturnData[4]; let schemeProposalInfo = { @@ -1406,29 +1404,33 @@ export default class UtilsService { if ( votingMachineExecutionEvent.length > 0 && - votingMachineExecutionEvent[0].data === + votingMachineExecutionEvent[0].data !== '0x0000000000000000000000000000000000000000000000000000000000000001' ) - schemeProposalInfo.state = WalletSchemeProposalState.Submitted; - else if (votingMachineExecutionEvent.length > 0) { schemeProposalInfo.state = WalletSchemeProposalState.Rejected; - } - const executionEvent = await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - [ - '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', - avatarAddressEncoded, - proposalId, - ], - 10000000 - ); - if (executionEvent.length > 0) { - schemeProposalInfo.state = - WalletSchemeProposalState.ExecutionSucceded; + if ( + callsResponse.decodedReturnData[0].state === + VotingMachineProposalState.Executed && + schemeProposalInfo.state === WalletSchemeProposalState.Submitted + ) { + // event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId) + const executionEvent = await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + [ + '0x253ad9614c337848bbe7dc3b18b439d139ef5787282b5a517ba7296513d1f533', + avatarAddressEncoded, + proposalId, + ], + 10000000 + ); + if (executionEvent.length > 0) { + schemeProposalInfo.state = + WalletSchemeProposalState.ExecutionSucceded; + } } // If any of the values of the redeemPeriods of the contribution reward is higher than zero it means that it executed the reward @@ -1460,8 +1462,9 @@ export default class UtilsService { creationEvent.transactionHash ); try { - schemeTypeData.newProposalTopics.map((newProposalTopic, i) => { - transactionReceipt.logs.map(log => { + // Decode the creation event data to get the num of choices, paramsHash and proposer + schemeTypeData.newProposalTopics.forEach((newProposalTopic, i) => { + transactionReceipt.logs.forEach(log => { if ( log.topics[0] === '0x75b4ff136cc5de5957574c797de3334eb1c141271922b825eb071e0487ba2c5c' @@ -1572,7 +1575,7 @@ export default class UtilsService { schemeProposalInfo.value.push(0); // Remove the negative sign in the number - if (creationLogDecoded._reputationChange[0] == '-') + if (creationLogDecoded._reputationChange[0] === '-') creationLogDecoded._reputationChange = creationLogDecoded._reputationChange.substring(1); @@ -1810,8 +1813,8 @@ export default class UtilsService { daoRedeemItsWinnings: callsResponse.decodedReturnData[0].daoRedeemItsWinnings, shouldBoost: callsResponse.decodedReturnData[5][0], - positiveVotes: bnum(positiveVotes), - negativeVotes: bnum(negativeVotes), + positiveVotes: bnum(callsResponse.decodedReturnData[1][0]), + negativeVotes: bnum(callsResponse.decodedReturnData[2][0]), positiveStakes: bnum(callsResponse.decodedReturnData[3][2]), negativeStakes: bnum(callsResponse.decodedReturnData[3][3]), }; @@ -1869,8 +1872,12 @@ export default class UtilsService { callsResponse.decodedReturnData[0].daoRedeemItsWinnings; networkCache.proposals[proposalId].shouldBoost = callsResponse.decodedReturnData[5][0]; - networkCache.proposals[proposalId].positiveVotes = bnum(positiveVotes); - networkCache.proposals[proposalId].negativeVotes = bnum(negativeVotes); + networkCache.proposals[proposalId].positiveVotes = bnum( + callsResponse.decodedReturnData[1][0] + ); + networkCache.proposals[proposalId].negativeVotes = bnum( + callsResponse.decodedReturnData[2][0] + ); networkCache.proposals[proposalId].positiveStakes = bnum( callsResponse.decodedReturnData[3][2] ); From 06704c688de6d7413117543c2bffce88574bada3 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 00:04:50 -0300 Subject: [PATCH 43/67] fix(cacheservice): dony use any as type --- src/services/CacheService.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index dbc3fe8970..644dae7605 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -29,6 +29,7 @@ import { } from '../utils'; import { getContracts } from '../contracts'; +import { Contract } from 'ethers'; const Hash = require('ipfs-only-hash'); const jsonSort = require('json-keys-sort'); @@ -292,7 +293,7 @@ export default class UtilsService { // Get all Mint and Burn reputation events async updateReputationEvents( networkCache: DaoNetworkCache, - reputation: any, + reputation: Contract, toBlock: number, web3: Web3 ): Promise { @@ -396,7 +397,7 @@ export default class UtilsService { async updateVotingMachine( networkCache: DaoNetworkCache, networkContracts: NetworkContracts, - votingMachine: any, + votingMachine: Contract, toBlock: number, web3: Web3 ): Promise { @@ -1230,7 +1231,7 @@ export default class UtilsService { web3: Web3, fromBlock: number, toBlock: number, - creationEvent?: any + creationEvent?: BlockchainEvent ): Promise { const newProposal = !networkCache.proposals[proposalId]; const schemeAddress = newProposal @@ -1246,7 +1247,7 @@ export default class UtilsService { ); // These first calls target mainly the voting machine where most of the proposal information is mutable - let callsToExecute: any[] = [ + let callsToExecute = [ [ networkCache.schemes[schemeAddress].votingMachine, 'proposals(bytes32)', @@ -1459,7 +1460,7 @@ export default class UtilsService { if (newProposal) { if (creationEvent && !isWalletScheme(schemeOfProposal)) { const transactionReceipt = await web3.eth.getTransactionReceipt( - creationEvent.transactionHash + creationEvent.tx ); try { // Decode the creation event data to get the num of choices, paramsHash and proposer @@ -1785,7 +1786,7 @@ export default class UtilsService { event: creationEvent.event, signature: creationEvent.signature, address: creationEvent.address, - tx: creationEvent.transactionHash, + tx: creationEvent.tx, blockNumber: creationEvent.blockNumber, timestamp: creationEvent.timestamp, transactionIndex: creationEvent.transactionIndex, @@ -1825,7 +1826,7 @@ export default class UtilsService { event: creationEvent.event, signature: creationEvent.signature, address: creationEvent.address, - tx: creationEvent.transactionHash, + tx: creationEvent.tx, blockNumber: creationEvent.blockNumber, timestamp: creationEvent.timestamp, transactionIndex: creationEvent.transactionIndex, From ecf96b5ea0948d9d1ea208dc8148941cf013f97b Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 00:07:19 -0300 Subject: [PATCH 44/67] refactor(cacheservice): more readable return in processProposals --- src/services/CacheService.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 644dae7605..b27a2aea21 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -822,20 +822,19 @@ export default class UtilsService { await batchPromisesOntarget( Object.keys(networkCache.proposals) .filter(proposalId => { + const proposal = networkCache.proposals[proposalId]; + const scheme = networkCache.schemes[proposal.scheme]; return ( // Only update proposals for registered schemes - networkCache.schemes[networkCache.proposals[proposalId].scheme] - .registered && + scheme.registered && // This condition check that the proposal already existed in the current block range. // If not, it means that the proposal was created in the current block range when processing the scheme, // So there is no need to process it again. - networkCache.proposals[proposalId].creationEvent.blockNumber < - fromBlock && + proposal.creationEvent.blockNumber < fromBlock && // This condition check that the proposal is active - (networkCache.proposals[proposalId].stateInVotingMachine > + (proposal.stateInVotingMachine > VotingMachineProposalState.Executed || - networkCache.proposals[proposalId].stateInScheme === - WalletSchemeProposalState.Submitted) + proposal.stateInScheme === WalletSchemeProposalState.Submitted) ); }) .map(proposalId => { From 97e931ccfee1013cce4e716dd240c662c533eb18 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 00:09:17 -0300 Subject: [PATCH 45/67] feat(utils): add maxTries to retryPromise helper --- src/utils/helpers.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index bef93df48f..12540fc2b6 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -90,16 +90,21 @@ export function escapeRegExp(string: string): string { export async function retryPromise( promise: Promise, - timeToWait: number = 0 + timeToWait: number = 0, + maxTries: number = 5 ): Promise { let toReturn; - while (!toReturn) { + while (!toReturn && maxTries > 0) { try { toReturn = await promise; - if (!toReturn) await sleep(timeToWait); + if (!toReturn) { + await sleep(timeToWait); + maxTries--; + } } catch (error) { console.warn(error); await sleep(timeToWait); + maxTries--; } } return toReturn; From 533ec56f8b4481b7c3cc70b317f5447a59e697bf Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 00:34:35 -0300 Subject: [PATCH 46/67] refactor(utils): maxTries in promise helpers --- src/utils/cache.ts | 13 +++++-------- src/utils/helpers.ts | 5 ++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 5e25102a54..d48360d027 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -328,20 +328,16 @@ export async function batchPromisesOntarget( promises, targetObject, maxPromisesPerTry = 0, - maxErrorsTry = 5 + maxTries = 10 ) { let promisesBatch = maxPromisesPerTry === 0 ? [promises] : []; let promisesBatchIndex = 0; - let errorsTry = 0; if (maxPromisesPerTry > 0) for (var i = 0; i < promises.length; i += maxPromisesPerTry) promisesBatch.push(promises.slice(i, i + maxPromisesPerTry)); - while ( - promisesBatchIndex < promisesBatch.length && - errorsTry < maxErrorsTry - ) { + while (promisesBatchIndex < promisesBatch.length && maxTries > 0) { try { (await Promise.all(promisesBatch[promisesBatchIndex])).map( targetObjectUpdated => { @@ -351,10 +347,11 @@ export async function batchPromisesOntarget( promisesBatchIndex++; } catch (e) { console.error(e); - errorsTry++; + maxTries--; } finally { - if (errorsTry >= maxErrorsTry) + if (maxTries === 0) console.error('[BATCH PROMISES] (max errors reached)'); + else maxTries = 0; } } return targetObject; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 12540fc2b6..acb9daf43b 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -91,7 +91,7 @@ export function escapeRegExp(string: string): string { export async function retryPromise( promise: Promise, timeToWait: number = 0, - maxTries: number = 5 + maxTries: number = 10 ): Promise { let toReturn; while (!toReturn && maxTries > 0) { @@ -105,6 +105,9 @@ export async function retryPromise( console.warn(error); await sleep(timeToWait); maxTries--; + } finally { + if (maxTries === 0) console.error('[RETRY PROMISE] (max errors reached)'); + else maxTries = 0; } } return toReturn; From a91fe268731e96457882ffa720d46ada7532b2bb Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 00:37:22 -0300 Subject: [PATCH 47/67] fix(cacheservice): fetch all values when adding scheme for the first time --- src/services/CacheService.ts | 99 +++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index b27a2aea21..aa58e4c877 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -980,16 +980,8 @@ export default class UtilsService { ], ]; - if (isNewScheme && isWalletScheme) { - schemeType = ( - await executeMulticall(networkWeb3Contracts.multicall, [ - [schemeAddress, 'SCHEME_TYPE()', [], ['string']], - ]) - ).decodedReturnData[0][0]; - if (schemeType === 'Wallet Scheme v1') schemeType = 'Wallet Scheme v1.0'; - - callsToExecute.push([schemeAddress, 'votingMachine()', [], ['address']]); - callsToExecute.push([schemeAddress, 'schemeName()', [], ['string']]); + if (isWalletScheme) { + console.log('Processing Wallet Scheme'); callsToExecute.push([ schemeAddress, 'maxSecondsForExecution()', @@ -1003,23 +995,41 @@ export default class UtilsService { ['uint256'], ]); - switch (schemeType) { - case 'Wallet Scheme v1.0': - callsToExecute.push([ - schemeAddress, - 'controllerAddress()', - [], - ['address'], - ]); - break; - default: - callsToExecute.push([ - schemeAddress, - 'doAvatarGenericCalls()', - [], - ['bool'], - ]); - break; + if (isNewScheme) { + schemeType = ( + await executeMulticall(networkWeb3Contracts.multicall, [ + [schemeAddress, 'SCHEME_TYPE()', [], ['string']], + ]) + ).decodedReturnData[0][0]; + if (schemeType === 'Wallet Scheme v1') + schemeType = 'Wallet Scheme v1.0'; + + callsToExecute.push([ + schemeAddress, + 'votingMachine()', + [], + ['address'], + ]); + callsToExecute.push([schemeAddress, 'schemeName()', [], ['string']]); + + switch (schemeType) { + case 'Wallet Scheme v1.0': + callsToExecute.push([ + schemeAddress, + 'controllerAddress()', + [], + ['address'], + ]); + break; + default: + callsToExecute.push([ + schemeAddress, + 'doAvatarGenericCalls()', + [], + ['bool'], + ]); + break; + } } } @@ -1037,23 +1047,26 @@ export default class UtilsService { const votingMachineAddress = !isNewScheme ? networkCache.schemes[schemeAddress].votingMachine : isWalletScheme - ? callsResponse1.decodedReturnData[2][0] + ? callsResponse1.decodedReturnData[4][0] : schemeTypeData.votingMachine; - if (isNewScheme && isWalletScheme) { - schemeName = callsResponse1.decodedReturnData[3][0]; - maxSecondsForExecution = callsResponse1.decodedReturnData[4][0]; - maxRepPercentageChange = callsResponse1.decodedReturnData[5][0]; + if (isWalletScheme) { + maxSecondsForExecution = callsResponse1.decodedReturnData[2][0]; + maxRepPercentageChange = callsResponse1.decodedReturnData[3][0]; - switch (schemeType) { - case 'Wallet Scheme v1.0': - controllerAddress = callsResponse1.decodedReturnData[6][0]; - break; - default: - controllerAddress = callsResponse1.decodedReturnData[6][0] - ? networkWeb3Contracts.controller._address - : ZERO_ADDRESS; - break; + if (isNewScheme) { + schemeName = callsResponse1.decodedReturnData[5][0]; + + switch (schemeType) { + case 'Wallet Scheme v1.0': + controllerAddress = callsResponse1.decodedReturnData[6][0]; + break; + default: + controllerAddress = callsResponse1.decodedReturnData[6][0] + ? networkWeb3Contracts.controller._address + : ZERO_ADDRESS; + break; + } } } @@ -1161,7 +1174,7 @@ export default class UtilsService { boostedVoteRequiredPercentage, proposalIds: [], boostedProposals: boostedProposals, - maxSecondsForExecution: maxSecondsForExecution, + maxSecondsForExecution, maxRepPercentageChange, newProposalEvents: [], }; @@ -1169,6 +1182,8 @@ export default class UtilsService { networkCache.schemes[schemeAddress].boostedProposals = boostedProposals; networkCache.schemes[schemeAddress].maxSecondsForExecution = maxSecondsForExecution; + networkCache.schemes[schemeAddress].maxRepPercentageChange = + maxRepPercentageChange; networkCache.schemes[schemeAddress].boostedVoteRequiredPercentage = boostedVoteRequiredPercentage; networkCache.schemes[schemeAddress].paramsHash = paramsHash; From 90856525c025ae45099aedc548881c4f26faa103 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 01:40:19 -0300 Subject: [PATCH 48/67] feat(web3reactmanager): show loading icon while trying to eager connect --- src/components/Header/loadingNetwork.tsx | 51 +++++++++++++++++++++++ src/components/Web3ReactManager/index.tsx | 38 +++++++++++++---- 2 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 src/components/Header/loadingNetwork.tsx diff --git a/src/components/Header/loadingNetwork.tsx b/src/components/Header/loadingNetwork.tsx new file mode 100644 index 0000000000..8bd4712e77 --- /dev/null +++ b/src/components/Header/loadingNetwork.tsx @@ -0,0 +1,51 @@ +import { observer } from 'mobx-react'; +import styled from 'styled-components'; +import dxdaoIcon from 'assets/images/DXdao.svg'; + +const NavWrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + padding: 20px 0px 0px 0px; +`; + +const NavSection = styled.div` + display: flex; + flex-direction: row; + align-items: center; +`; + +const MenuItem = styled.div` + display: flex; + align-items: center; + color: var(--nav-text-light); + font-size: 16px; + line-height: 19px; + cursor: pointer; +`; + +const WarningDev = styled.div` + margin-left: 5px; + padding-top: 3px; + color: red; +`; + +const LoadingNetworkHeader = observer(() => { + const isTestingEnv = !window?.location?.href?.includes('dxvote.eth'); + + return ( + + + <> + + dxdao + {isTestingEnv && Testing Environment} + + + + + ); +}); + +export default LoadingNetworkHeader; diff --git a/src/components/Web3ReactManager/index.tsx b/src/components/Web3ReactManager/index.tsx index 9cb116432b..5b006d3df8 100644 --- a/src/components/Web3ReactManager/index.tsx +++ b/src/components/Web3ReactManager/index.tsx @@ -7,6 +7,11 @@ import { useContext } from 'contexts'; import { DEFAULT_CHAIN_ID, useInterval, usePrevious } from 'utils'; import { InjectedConnector } from '@web3-react/injected-connector'; import { NetworkConnector } from '@web3-react/network-connector'; +import ThemeProvider, { GlobalStyle } from 'theme'; +import styled from 'styled-components'; +import LoadingNetworkHeader from '../Header/loadingNetwork'; +import { LoadingBox } from '../../pages/proposals/styles'; +import PulsingIcon from 'components/common/LoadingIcon'; const BLOKCHAIN_FETCH_INTERVAL = 10000; @@ -142,21 +147,40 @@ const Web3ReactManager = ({ children }) => { networkActive ? BLOKCHAIN_FETCH_INTERVAL : 10 ); + const Content = styled.div` + margin: auto; + height: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + width: 85%; + `; + // on page load, do nothing until we've tried to connect to the injected connector if (!triedEager) { console.debug('[Web3ReactManager] Render: Eager load not tried'); - return null; - } - if (networkError) { + return ( + + + + + +
+ +
+
+
+
+ ); + } else if (networkError) { console.debug( '[Web3ReactManager] Render: Network error, showing modal error.' ); return null; } else { - console.debug( - '[Web3ReactManager] Render: Active network, render children', - { networkActive } - ); + console.debug('[Web3ReactManager] Render: Render children', { + networkActive, + }); return children; } }; From 397d4a8f54e3a7058a9bf86e6c2dd1432b1f52c8 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Thu, 21 Apr 2022 12:47:47 -0300 Subject: [PATCH 49/67] fix(cacheservice): decode creation event --- src/services/CacheService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index aa58e4c877..74c332b940 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -981,7 +981,6 @@ export default class UtilsService { ]; if (isWalletScheme) { - console.log('Processing Wallet Scheme'); callsToExecute.push([ schemeAddress, 'maxSecondsForExecution()', @@ -1218,6 +1217,8 @@ export default class UtilsService { ? schemeEvent.topics[2] : schemeEvent.topics[1]; + schemeEvent.tx = schemeEvent.transactionHash; + return this.processProposal( proposalId, networkCache, From 9fbe4351712f0165619de1d22ad05d318747c556 Mon Sep 17 00:00:00 2001 From: rossneilson Date: Fri, 22 Apr 2022 10:17:45 +0100 Subject: [PATCH 50/67] fix: Actually use pinata service --- src/services/IPFSService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/services/IPFSService.ts b/src/services/IPFSService.ts index d5bcff1bd1..208c00dd4f 100644 --- a/src/services/IPFSService.ts +++ b/src/services/IPFSService.ts @@ -95,8 +95,10 @@ export default class IPFSService { localStorage.setItem('dxvote-newProposal-hash', hash); if (pinataService.auth) { - const pinataPin = await this.pin(hash); + const pinataPin = await pinataService.pin(hash); console.debug('[PINATA PIN]', pinataPin.toString()); + } else { + console.debug('[PINATA PIN] NOT AUTHENTICATED'); } const ipfsPin = await this.pin(hash); console.debug('[IPFS PIN]', ipfsPin); @@ -115,7 +117,7 @@ export default class IPFSService { const hash = await this.add(content); if (this.context.pinataService.auth) { - const pinataPin = await this.pin(hash); + const pinataPin = await this.context.pinataService.pin(hash); console.debug('[PINATA PIN]', pinataPin.toString()); } const ipfsPin = await this.pin(hash); From 84a4f9b2422d4ed4ed3c8f2896d10d7674c9350e Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 4 May 2022 09:57:13 -0300 Subject: [PATCH 51/67] fix(scripts): compile contracts before start dev script --- scripts/dev.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/dev.sh b/scripts/dev.sh index 081b959ad0..caf451f745 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -20,6 +20,8 @@ hardhat_running() { start-hardhat_node() { + yarn hardhat compile + npx hardhat node > /dev/null & # Account #0: 0x79706c8e413cdaee9e63f282507287b9ea9c0928 (10000 ETH) From d929edd39366d0f40c212c069c5174252b7dbade Mon Sep 17 00:00:00 2001 From: AugustoL Date: Fri, 6 May 2022 10:55:17 -0300 Subject: [PATCH 52/67] refactor(services): remove not used message logger service --- src/components/Proposal/Votes/index.tsx | 63 +------------- src/contexts/index.ts | 3 - src/services/MessageLoggerService.ts | 109 ------------------------ 3 files changed, 1 insertion(+), 174 deletions(-) delete mode 100644 src/services/MessageLoggerService.ts diff --git a/src/components/Proposal/Votes/index.tsx b/src/components/Proposal/Votes/index.tsx index a7f957f847..f601cda488 100644 --- a/src/components/Proposal/Votes/index.tsx +++ b/src/components/Proposal/Votes/index.tsx @@ -37,7 +37,6 @@ import { verifySignedVote, } from 'utils'; import { utils } from 'ethers'; -import useJsonRpcProvider from 'hooks/Guilds/web3/useJsonRpcProvider'; const Votes = () => { const { @@ -46,7 +45,6 @@ const Votes = () => { daoStore, providerStore, daoService, - messageLoggerService, orbitDBService, }, } = useContext(); @@ -56,8 +54,6 @@ const Votes = () => { const [decision, setDecision] = useState(0); const [votePercentage, setVotePercentage] = useState(0); const [signedVotesOfProposal, setSignedVotesOfProposal] = useState([]); - const [loadingSignedRinkebyVotes, setLoadingSignedRinkebyVotes] = - useState(true); const [loadingSignedOrbitDBVotes, setLoadingSignedOrbitDBVotes] = useState(true); @@ -77,57 +73,6 @@ const Votes = () => { votingMachineOfProposal.address ].type == 'DXDVotingMachine'; - const rinkebyProvider = useJsonRpcProvider(4); - - messageLoggerService - .getMessages(signedVoteMessageId, rinkebyProvider) - .then(messagesEvents => { - console.debug('[Rinkeby Proposal messages]', messagesEvents); - messagesEvents.map(messagesEvent => { - if ( - messagesEvent.args && - messagesEvent.args.message.length > 0 && - messagesEvent.args.message.split(':').length > 6 - ) { - const signedVote = messagesEvent.args.message.split(':'); - const validSignature = verifySignedVote( - signedVote[1], - signedVote[2], - signedVote[3], - signedVote[4], - signedVote[5], - signedVote[6] - ); - - const alreadyAdded = - signedVotesOfProposal.findIndex(s => s.voter == signedVote[3]) > - -1 || - proposalEvents.votes.findIndex(s => s.voter == signedVote[3]) > -1; - - const repOfVoterForProposal = daoStore.getRepAt( - signedVote[3], - proposal.creationEvent.blockNumber - ).userRep; - - if ( - validSignature && - !alreadyAdded && - repOfVoterForProposal >= bnum(signedVote[5]) - ) { - signedVotesOfProposal.push({ - voter: signedVote[3], - vote: signedVote[4], - amount: bnum(signedVote[5]), - signature: signedVote[6], - source: 'rinkeby', - }); - } - } - }); - setSignedVotesOfProposal(signedVotesOfProposal); - setLoadingSignedRinkebyVotes(false); - }); - orbitDBService.getLogs(signedVoteMessageId).then(signedVoteMessages => { console.debug('[OrbitDB messages]', signedVoteMessages); signedVoteMessages.map(signedVoteMessageRaw => { @@ -272,12 +217,6 @@ const Votes = () => { utils.id(`dxvote:${proposalId}`), `signedVote:${voteDetails.votingMachine}:${voteDetails.proposalId}:${voteDetails.voter}:${voteDetails.decision}:${voteDetails.repAmount}:${voteSignature}` ); - if (voteDetails.networks[1]) - messageLoggerService.broadcast( - utils.id(`dxvote:${proposalId}`), - `signedVote:${voteDetails.votingMachine}:${voteDetails.proposalId}:${voteDetails.voter}:${voteDetails.decision}:${voteDetails.repAmount}:${voteSignature}`, - rinkebyProvider - ); } } else { daoService.vote( @@ -358,7 +297,7 @@ const Votes = () => { - {!loadingSignedRinkebyVotes && !loadingSignedOrbitDBVotes && ( + {!loadingSignedOrbitDBVotes && (
diff --git a/src/contexts/index.ts b/src/contexts/index.ts index 4bb71cef4c..bc04b900b2 100644 --- a/src/contexts/index.ts +++ b/src/contexts/index.ts @@ -14,7 +14,6 @@ import CustomRpcService from '../services/CustomRpcService'; import ENSService from '../services/ENSService'; import TokenVestingService from '../services/TokenVestingService'; import SubgraphService from '../services/SubgraphService'; -import MessageLoggerService from '../services/MessageLoggerService'; import OrbitDBService from '../services/OrbitDBService'; import ProviderStore from '../stores/Provider'; @@ -62,7 +61,6 @@ export default class RootContext { ensService: ENSService; tokenVestingService: TokenVestingService; subgraphService: SubgraphService; - messageLoggerService: MessageLoggerService; orbitDBService: OrbitDBService; cacheService: CacheService; @@ -89,7 +87,6 @@ export default class RootContext { this.ensService = new ENSService(this); this.tokenVestingService = new TokenVestingService(this); this.subgraphService = new SubgraphService(this); - this.messageLoggerService = new MessageLoggerService(this); this.orbitDBService = new OrbitDBService(this); this.cacheService = new CacheService(this); } diff --git a/src/services/MessageLoggerService.ts b/src/services/MessageLoggerService.ts deleted file mode 100644 index 5343151725..0000000000 --- a/src/services/MessageLoggerService.ts +++ /dev/null @@ -1,109 +0,0 @@ -import Common, { Chain, Hardfork } from '@ethereumjs/common'; -import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx'; -import RootContext from '../contexts'; -import { arrayBufferHex } from 'utils'; -import { ethers, utils } from 'ethers'; -import { JsonRpcProvider } from '@ethersproject/providers'; - -export default class MessageLoggerService { - context: RootContext; - - messageLoggerAddress: string = '0xA490faF0DC4F26101a15bAc6ECad55b59db014a7'; - messageLoggerABI: ethers.utils.Interface = new ethers.utils.Interface([ - 'event Message(bytes32 indexed topic, string message, address sender)', - 'function broadcast(bytes32 topic, string message)', - ]); - fromBlock: number = 9904867; - - constructor(context: RootContext) { - this.context = context; - } - - async broadcast( - topic: string, - message: string, - rinkebyWeb3: JsonRpcProvider - ) { - const { account } = this.context.providerStore.getActiveWeb3React(); - const common = new Common({ - chain: Chain.Rinkeby, - hardfork: Hardfork.London, - }); - - // Step 1: Create the TX to send in rinkeby with the signature of the vote. - let txData = { - from: account, - data: this.messageLoggerABI.encodeFunctionData('broadcast', [ - topic, - message, - ]), - nonce: await rinkebyWeb3.getTransactionCount(account), - gasLimit: 50000, - maxPriorityFeePerGas: 1000000000, - maxFeePerGas: 1000000000, - to: this.messageLoggerAddress, - value: 0, - type: '0x02', - chainId: '0x04', - DEFAULT_CHAIN: 'rinkeby', - }; - - const tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common }); - const unsignedTx = tx.getMessageToSign(false); - console.log('Unsigned Rinkeby tx:', tx); - - // Step 2: Sign the transaction with the vote signature to be shared in rinkeby executing a tx in rinkeby network - let signature = await this.context.providerStore.sign( - this.context.providerStore.getActiveWeb3React(), - utils.keccak256('0x' + arrayBufferHex(unsignedTx)) - ); - - // Step 3: Send the raw transaction signed to the rinkeby network - if (signature.result) { - signature = signature.result.substr(2); - const r = '0x' + signature.substr(0, 64); - const s = '0x' + signature.substr(64, 64); - const v = signature.substr(128) == '1c' ? '0x1' : '0x0'; - console.log('Rinkeby tx object:', txData); - console.log(`Rinkeby tx signature: ${v}${r}${s}`); - - const signedTx = FeeMarketEIP1559Transaction.fromTxData({ - ...txData, - v, - r, - s, - }); - const from = signedTx.getSenderAddress().toString(); - console.log( - `Signed Rinkeby tx hex: ${signedTx - .serialize() - .toString('hex')}\n Rinkeby tx Signer: ${from}` - ); - - rinkebyWeb3.send('eth_sendRawTransaction', [ - '0x' + signedTx.serialize().toString('hex'), - ]); - } - } - - async getMessages(topic: string, rinkebyWeb3: JsonRpcProvider) { - const messageLogger = new ethers.Contract( - this.messageLoggerAddress, - this.messageLoggerABI, - rinkebyWeb3 - ); - - const filter = messageLogger.filters.Message(topic); - try { - const events = await messageLogger.queryFilter( - filter, - this.fromBlock, - await rinkebyWeb3.getBlockNumber() - ); - return events; - } catch (error) { - console.error('Error fetching message logger events', error); - return []; - } - } -} From 9f0c1a52036642d1641ed9ac41808cbc07fc7bc3 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Fri, 6 May 2022 10:58:36 -0300 Subject: [PATCH 53/67] fix(cacheservice): optmize fecth of scheme events --- src/services/CacheService.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 74c332b940..80c8c061ea 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1197,18 +1197,13 @@ export default class UtilsService { ); const fromBlock = networkCache.blockNumber + 1; - let schemeEvents = []; - for (let i = 0; i < schemeTypeData.newProposalTopics.length; i++) { - schemeEvents = schemeEvents.concat( - await getRawEvents( - web3, - schemeAddress, - fromBlock, - toBlock, - schemeTypeData.newProposalTopics[i] - ) - ); - } + let schemeEvents = await getRawEvents( + web3, + schemeAddress, + fromBlock, + toBlock, + schemeTypeData.newProposalTopics + ); await batchPromisesOntarget( schemeEvents.map(schemeEvent => { From b5cb2e3731f5ddcfee2fbedeb01b205e8e813b8a Mon Sep 17 00:00:00 2001 From: AugustoL Date: Fri, 6 May 2022 10:59:20 -0300 Subject: [PATCH 54/67] fix(pages/config): reload app on clear cache --- src/pages/Configuration.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Configuration.tsx b/src/pages/Configuration.tsx index 468fc4a162..48f630a56f 100644 --- a/src/pages/Configuration.tsx +++ b/src/pages/Configuration.tsx @@ -110,6 +110,7 @@ const ConfigPage = observer(() => { async function clearCache() { localStorage.clear(); caches.delete(`dxvote-cache`); + window.location.reload(); } return ( From 968b50c960a8565a225d16e58ea500f5c3aee4b7 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Fri, 6 May 2022 11:01:00 -0300 Subject: [PATCH 55/67] fix(cacheservice): fix detection of proposal rejected --- src/services/CacheService.ts | 35 ++++++++++++++++++----------------- src/utils/proposals.ts | 30 ++---------------------------- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 80c8c061ea..85f40edd65 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1377,19 +1377,20 @@ export default class UtilsService { let decodedProposer; let creationLogDecoded; - if (newProposal && isWalletScheme(schemeOfProposal)) { - // @ts-ignore - schemeProposalInfo.to = callsResponse.decodedReturnData[6].to; - schemeProposalInfo.callData = callsResponse.decodedReturnData[6].callData; - schemeProposalInfo.value = callsResponse.decodedReturnData[6].value; - schemeProposalInfo.state = callsResponse.decodedReturnData[6].state; - schemeProposalInfo.title = callsResponse.decodedReturnData[6].title; - schemeProposalInfo.descriptionHash = - callsResponse.decodedReturnData[6].descriptionHash; - schemeProposalInfo.submittedTime = - callsResponse.decodedReturnData[6].submittedTime; - } else if (isWalletScheme(schemeOfProposal)) { + if (isWalletScheme(schemeOfProposal)) { schemeProposalInfo.state = callsResponse.decodedReturnData[6].state; + + if (newProposal) { + schemeProposalInfo.to = callsResponse.decodedReturnData[6].to; + schemeProposalInfo.callData = + callsResponse.decodedReturnData[6].callData; + schemeProposalInfo.value = callsResponse.decodedReturnData[6].value; + schemeProposalInfo.title = callsResponse.decodedReturnData[6].title; + schemeProposalInfo.descriptionHash = + callsResponse.decodedReturnData[6].descriptionHash; + schemeProposalInfo.submittedTime = + callsResponse.decodedReturnData[6].submittedTime; + } } else { // Here we get the events triggered in the GenericMulticall to get their final state // When the proposal is executed by the voting machine we get if the proposal was rejected @@ -1447,6 +1448,11 @@ export default class UtilsService { // If any of the values of the redeemPeriods of the contribution reward is higher than zero it means that it executed the reward } else if (schemeOfProposal.type === 'ContributionReward') { if ( + callsResponse.decodedReturnData[0].winningVote === '2' && + callsResponse.decodedReturnData[0].state === '2' + ) { + schemeProposalInfo.state = WalletSchemeProposalState.Rejected; + } else if ( callsResponse.decodedReturnData[6][0] > 0 || callsResponse.decodedReturnData[7][0] > 0 || callsResponse.decodedReturnData[8][0] > 0 || @@ -1454,11 +1460,6 @@ export default class UtilsService { ) { schemeProposalInfo.state = WalletSchemeProposalState.ExecutionSucceded; - } else if ( - callsResponse.decodedReturnData[0].state === '1' || - callsResponse.decodedReturnData[0].state === '2' - ) { - schemeProposalInfo.state = WalletSchemeProposalState.Rejected; } else { schemeProposalInfo.state = WalletSchemeProposalState.Submitted; } diff --git a/src/utils/proposals.ts b/src/utils/proposals.ts index 0d2f65668e..57988b3ff3 100644 --- a/src/utils/proposals.ts +++ b/src/utils/proposals.ts @@ -200,29 +200,7 @@ export const decodeProposalStatus = function ( pendingAction: PendingAction.None, }; case VotingMachineProposalState.Executed: - if ( - proposal.stateInScheme === WalletSchemeProposalState.Rejected && - schemeType === 'ContributionReward' - ) - return { - status: 'Passed', - boostTime: boostedPhaseTime, - finishTime: proposalStateChangeEvents.find( - event => Number(event.state) === VotingMachineProposalState.Executed - ) - ? bnum( - proposalStateChangeEvents.find( - event => - Number(event.state) === VotingMachineProposalState.Executed - ).timestamp - ) - : bnum(0), - pendingAction: PendingAction.Redeem, - }; - else if ( - proposal.winningVote == '2' || - proposal.stateInScheme === WalletSchemeProposalState.Rejected - ) + if (proposal.stateInScheme === WalletSchemeProposalState.Rejected) return { status: 'Proposal Rejected', boostTime: boostedPhaseTime, @@ -236,11 +214,7 @@ export const decodeProposalStatus = function ( ).timestamp ) : bnum(0), - pendingAction: - schemeType === 'ContributionReward' && - proposal.stateInVotingMachine < 3 - ? PendingAction.Redeem - : PendingAction.None, + pendingAction: PendingAction.None, }; else if ( proposal.stateInScheme === WalletSchemeProposalState.ExecutionSucceded From 4d600e0654c21d1d534ca427cc0d474b7ee8bf8d Mon Sep 17 00:00:00 2001 From: Dino Date: Mon, 9 May 2022 13:25:05 -0300 Subject: [PATCH 56/67] Added network name to route in back to proposals button --- src/pages/ProposalSubmission/Custom.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/ProposalSubmission/Custom.tsx b/src/pages/ProposalSubmission/Custom.tsx index 58ad10e984..b8c456af1d 100644 --- a/src/pages/ProposalSubmission/Custom.tsx +++ b/src/pages/ProposalSubmission/Custom.tsx @@ -86,6 +86,7 @@ const NewProposalPage = observer(() => { }, } = useContext(); + const networkName = configStore.getActiveChainName(); const schemes = daoStore.getAllSchemes(); const networkContracts = configStore.getNetworkContracts(); const schemeInLocalStorage = localStorage.getItem( @@ -720,7 +721,9 @@ const NewProposalPage = observer(() => { active={submitionState} /> ) : ( - Back to Proposals + + Back to Proposals + )} {submitionState > 1 ? ( From e7fd3ee9ec769f77f42e92101a45e4a0df349317 Mon Sep 17 00:00:00 2001 From: rossneilson Date: Tue, 10 May 2022 20:58:01 +0100 Subject: [PATCH 57/67] feat: Add pinata pro gateway and key --- src/services/IPFSService.ts | 10 ++++++++-- src/services/PinataService.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/services/IPFSService.ts b/src/services/IPFSService.ts index d5bcff1bd1..d1c4103c31 100644 --- a/src/services/IPFSService.ts +++ b/src/services/IPFSService.ts @@ -50,6 +50,10 @@ export default class IPFSService { async getContentFromIPFS(hash: string) { const response = await Promise.any([ + axios.request({ + url: 'https://dxgov.mypinata.cloud/' + hash, + method: 'GET', + }), axios.request({ url: 'https://ipfs.io/ipfs/' + hash, method: 'GET', @@ -95,8 +99,10 @@ export default class IPFSService { localStorage.setItem('dxvote-newProposal-hash', hash); if (pinataService.auth) { - const pinataPin = await this.pin(hash); + const pinataPin = await pinataService.pin(hash); console.debug('[PINATA PIN]', pinataPin.toString()); + } else { + console.debug('[PINATA PIN] NOT AUTHENTICATED'); } const ipfsPin = await this.pin(hash); console.debug('[IPFS PIN]', ipfsPin); @@ -115,7 +121,7 @@ export default class IPFSService { const hash = await this.add(content); if (this.context.pinataService.auth) { - const pinataPin = await this.pin(hash); + const pinataPin = await this.context.pinataService.pin(hash); console.debug('[PINATA PIN]', pinataPin.toString()); } const ipfsPin = await this.pin(hash); diff --git a/src/services/PinataService.ts b/src/services/PinataService.ts index 3624d98321..9a09b2ca77 100644 --- a/src/services/PinataService.ts +++ b/src/services/PinataService.ts @@ -10,7 +10,7 @@ export default class PinataService { this.context = context; } defaultApiKey = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiI4ZTNlZjUzNi0wZWQ5LTQ4YzAtOTFlYS1kNzUwYjk0Nzk4ZDMiLCJlbWFpbCI6Im1lQHJvc3NuZWlsc29uLmRldiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaW5fcG9saWN5Ijp7InJlZ2lvbnMiOlt7ImlkIjoiRlJBMSIsImRlc2lyZWRSZXBsaWNhdGlvbkNvdW50IjoxfV0sInZlcnNpb24iOjF9LCJtZmFfZW5hYmxlZCI6ZmFsc2V9LCJhdXRoZW50aWNhdGlvblR5cGUiOiJzY29wZWRLZXkiLCJzY29wZWRLZXlLZXkiOiJlNmM5ZDA4ZGY3ODY0YWRhZWIyMyIsInNjb3BlZEtleVNlY3JldCI6ImRmYmY4ZmNiNGQ0YjQxNWViODgyZDM1YjgyMDFlMDVjNjk1MjBkZDllOTg2MzgxZTY3YTI1YTk2N2YyOWQxOGQiLCJpYXQiOjE2Mzk1OTg2MTl9.tkenai9BlBubfnPJmIXz9DkjJg12aCyk3BAtAc-TU1A'; + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiI4MTJmMzIwZC1iOTA1LTQwOTgtYmViZC1jMjMwNzhlNDNmM2MiLCJlbWFpbCI6ImZsdWlkZHJvcDU2NDgyM0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicGluX3BvbGljeSI6eyJyZWdpb25zIjpbeyJpZCI6Ik5ZQzEiLCJkZXNpcmVkUmVwbGljYXRpb25Db3VudCI6MX1dLCJ2ZXJzaW9uIjoxfSwibWZhX2VuYWJsZWQiOmZhbHNlfSwiYXV0aGVudGljYXRpb25UeXBlIjoic2NvcGVkS2V5Iiwic2NvcGVkS2V5S2V5IjoiYmFhMjllYjUxZWYzZWQyMDY4MWEiLCJzY29wZWRLZXlTZWNyZXQiOiI0OTIyYzQ2MThhYWZlNzZmNzhiNWQzNzU0NzY4MjBiNTk1MWM5MjdkZjFiNzY3ZGI3OWUzMGY5OTI3MDBmYTc5IiwiaWF0IjoxNjUyMTg4MjAyfQ.c8CpCVxvdknULzW6dJALyWgHD_DMq5167Nlb1KkXNRI'; async updatePinList() { // const pinList = await this.getPinList(); From 29b910bf5b2a88188b1f9ee691c526055ddeda7f Mon Sep 17 00:00:00 2001 From: rossneilson Date: Thu, 12 May 2022 19:43:14 +0100 Subject: [PATCH 58/67] fix: Fix gateway link --- src/services/IPFSService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/IPFSService.ts b/src/services/IPFSService.ts index d1c4103c31..05cfc35461 100644 --- a/src/services/IPFSService.ts +++ b/src/services/IPFSService.ts @@ -51,7 +51,7 @@ export default class IPFSService { async getContentFromIPFS(hash: string) { const response = await Promise.any([ axios.request({ - url: 'https://dxgov.mypinata.cloud/' + hash, + url: 'https://dxgov.mypinata.cloud/ipfs/' + hash, method: 'GET', }), axios.request({ From 274c202df52c0e2bf2da965f974fe4f0f2d3b995 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 16 May 2022 13:08:01 -0300 Subject: [PATCH 59/67] fix(src/blockchainstore): retry to get cache from ipfs forever --- src/services/CacheService.ts | 12 ++++++++++++ src/services/IPFSService.ts | 10 ++++++---- src/stores/BlockchainStore.ts | 6 +++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 85f40edd65..32ffdd6185 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -41,6 +41,18 @@ export default class UtilsService { this.context = context; } + async getCacheFromIPFS(ipfsHash: string): Promise { + let jsonCache = await this.context.ipfsService.getContentFromIPFS(ipfsHash); + + while (typeof jsonCache === 'string') { + console.log('Trying to get cache from ipfs again'); + await sleep(100); + jsonCache = await this.context.ipfsService.getContentFromIPFS(ipfsHash); + } + + return jsonCache; + } + async getUpdatedCacheConfig( networksConfig: { [netwokId: number]: { diff --git a/src/services/IPFSService.ts b/src/services/IPFSService.ts index 05cfc35461..20f4d59409 100644 --- a/src/services/IPFSService.ts +++ b/src/services/IPFSService.ts @@ -53,30 +53,32 @@ export default class IPFSService { axios.request({ url: 'https://dxgov.mypinata.cloud/ipfs/' + hash, method: 'GET', + timeout: 60000, }), axios.request({ url: 'https://ipfs.io/ipfs/' + hash, method: 'GET', + timeout: 60000, }), axios.request({ url: 'https://gateway.ipfs.io/ipfs/' + hash, method: 'GET', + timeout: 60000, }), axios.request({ url: 'https://cloudflare-ipfs.com/ipfs/' + hash, method: 'GET', - }), - axios.request({ - url: 'https://gateway.pinata.cloud/ipfs/' + hash, - method: 'GET', + timeout: 60000, }), axios.request({ url: 'https://dweb.link/ipfs/' + hash, method: 'GET', + timeout: 60000, }), axios.request({ url: 'https://infura-ipfs.io/ipfs/' + hash, method: 'GET', + timeout: 60000, }), ]); return response.data; diff --git a/src/stores/BlockchainStore.ts b/src/stores/BlockchainStore.ts index 34678008b1..74de7ae043 100644 --- a/src/stores/BlockchainStore.ts +++ b/src/stores/BlockchainStore.ts @@ -38,7 +38,6 @@ export default class BlockchainStore { const { providerStore, configStore, - ipfsService, daoStore, notificationStore, cacheService, @@ -113,9 +112,10 @@ export default class BlockchainStore { true, 'Fetching cached data from IPFS' ); - networkCache = daoStore.parseCache( - await ipfsService.getContentFromIPFS(newestCacheIpfsHash) + const ipfsCache = await cacheService.getCacheFromIPFS( + newestCacheIpfsHash ); + networkCache = daoStore.parseCache(ipfsCache); networkCache.baseCacheIpfsHash = newestCacheIpfsHash; } From f6c0d5722223b5bcf954f36b4a6b0b426cc966ca Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 16 May 2022 13:10:47 -0300 Subject: [PATCH 60/67] fix(src/cacheservice): dont allow finish uncompleteCache try to generate good cache forever --- src/services/CacheService.ts | 32 ++++++++---- src/utils/cache.ts | 95 ++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 57 deletions(-) diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 32ffdd6185..72e432f4d3 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -248,10 +248,8 @@ export default class UtilsService { // The first promise round: // - Reputation Events - // - Voting Machine Events // - Permission Registry // - Vesting Contracts - // - Update schemes networkCache = await batchPromisesOntarget( [ @@ -261,19 +259,28 @@ export default class UtilsService { toBlock, web3 ), - this.updateVotingMachineEvents( + this.updatePermissionRegistry( networkCache, networkContracts, toBlock, web3 ), - this.updatePermissionRegistry( + this.updateVestingContracts( networkCache, networkContracts, toBlock, web3 ), - this.updateVestingContracts( + ], + networkCache + ); + + // The second promise round: + // - Voting Machine Events + // - Update schemes + networkCache = await batchPromisesOntarget( + [ + this.updateVotingMachineEvents( networkCache, networkContracts, toBlock, @@ -281,7 +288,9 @@ export default class UtilsService { ), this.updateSchemes(networkCache, networkContracts, toBlock, web3), ], - networkCache + networkCache, + 0, + 1000 ); this.context.notificationStore.setGlobalLoading( @@ -289,10 +298,13 @@ export default class UtilsService { `Collecting proposals in blocks ${fromBlock} - ${toBlock}` ); - // The second promise round is just to update the proposals that needs the schemes udpated. + // The third promise round is just to update the proposals that needs the schemes udpated. networkCache = await batchPromisesOntarget( [this.updateProposals(networkCache, networkContracts, toBlock, web3)], - networkCache + networkCache, + 0, + 1000, + 500 ); networkCache.blockNumber = Number(toBlock); @@ -860,7 +872,9 @@ export default class UtilsService { ); }), networkCache, - 5 + 5, + 1000, + 500 ); return networkCache; diff --git a/src/utils/cache.ts b/src/utils/cache.ts index d48360d027..0a060cb9b1 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -166,7 +166,7 @@ export const getRawEvents = async function ( (error as Error).message.indexOf('Relay attempts exhausted') === -1 && Math.trunc((to - from) / 2) > 10000 ) { - console.error('Error fetching blocks:', (error as Error).message); + console.error('Error fetching blocks:', error); const blocksToLower = Math.max(Math.trunc((to - from) / 2), 10000); console.debug('Lowering toBlock', blocksToLower, 'blocks'); to = to - blocksToLower; @@ -177,50 +177,21 @@ export const getRawEvents = async function ( }; export const getTimestampOfEvents = async function (web3, events) { - //// TODO: See how can we batch requests can be implemented - // async function batchRequest(blocks) { - // const batch = new web3.BatchRequest(); - // let requests = []; - // for (let i = 0; i < blocks.length; i++) { - // const request = new Promise((resolve, reject) => { - // batch.add(web3.eth.getBlock.request(blocks[i], (err, data) => { - // console.log(1) - // if (err) return reject(err); - // resolve(data); - // })); - // }); - // requests.push(request); - // } - // batch.execute(); - // console.log(batch) - // await Promise.all(requests); - // return batch; - // }; - - let blocksToFetch = []; - let timestamps = []; - events.map(event => { - if (blocksToFetch.indexOf(event.blockNumber) < 0) - blocksToFetch.push(event.blockNumber); - }); - const totalLength = blocksToFetch.length; - while (blocksToFetch.length > 0 && totalLength > timestamps.length) { - // timestamps = (await batchRequest(blocksToFetch)).map((blockResult) => { - // return blockResult.timestamp; - // }); - const blocksToFetchBatch = blocksToFetch.splice(0, 500); - await Promise.all( - blocksToFetchBatch.map(async block => { - const blockInfo = await web3.eth.getBlock(block); - for (let i = 0; i < events.length; i++) { - if (events[i].blockNumber === blockInfo.number) - events[i].timestamp = blockInfo.timestamp; - if (blockInfo.l2BlockNumber) - events[i].blockNumber = Number(blockInfo.l2BlockNumber); - } - }) - ); - } + await batchPromises( + events.map(async (event, i) => { + const blockInfo = await web3.eth.getBlock(event.blockNumber); + if (!blockInfo) console.log(event.blockNumber); + for (let i = 0; i < events.length; i++) { + if (events[i].blockNumber === blockInfo.number) + events[i].timestamp = blockInfo.timestamp; + if (blockInfo.l2BlockNumber) + events[i].blockNumber = Number(blockInfo.l2BlockNumber); + } + }), + 100, + 1000, + 500 + ); return events; }; @@ -328,7 +299,8 @@ export async function batchPromisesOntarget( promises, targetObject, maxPromisesPerTry = 0, - maxTries = 10 + maxTries = 10, + sleepTimeBetweenRetries = 100 ) { let promisesBatch = maxPromisesPerTry === 0 ? [promises] : []; let promisesBatchIndex = 0; @@ -348,11 +320,38 @@ export async function batchPromisesOntarget( } catch (e) { console.error(e); maxTries--; - } finally { + await sleep(sleepTimeBetweenRetries); if (maxTries === 0) console.error('[BATCH PROMISES] (max errors reached)'); - else maxTries = 0; } } return targetObject; } + +export async function batchPromises( + promises, + maxPromisesPerTry = 0, + maxTries = 10, + sleepTimeBetweenRetries = 100 +) { + let promisesBatch = maxPromisesPerTry === 0 ? [promises] : []; + let promisesBatchIndex = 0; + + if (maxPromisesPerTry > 0) + for (var i = 0; i < promises.length; i += maxPromisesPerTry) + promisesBatch.push(promises.slice(i, i + maxPromisesPerTry)); + + while (promisesBatchIndex < promisesBatch.length && maxTries > 0) { + try { + await Promise.all(promisesBatch[promisesBatchIndex]); + promisesBatchIndex++; + } catch (e) { + console.error(e); + maxTries--; + await sleep(sleepTimeBetweenRetries); + if (maxTries === 0) + console.error('[BATCH PROMISES] (max errors reached)'); + } + } + return; +} From 569013199df8d63bb2139e67034f51d7d59ad6a4 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 16 May 2022 13:43:55 -0300 Subject: [PATCH 61/67] fix(src/contracts): change null for 0x0 in WalletScheme new proposal topics --- src/utils/cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 0a060cb9b1..33f7374fe1 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -270,7 +270,7 @@ export const getSchemeConfig = function (networkContracts, schemeAddress) { newProposalTopics: [ [ Web3.utils.soliditySha3('ProposalStateChange(bytes32,uint256)'), - null, + '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', ], ], From fccfaf82ccc8fd7e32ce8f84cbceb01e0e161031 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 16 May 2022 20:38:21 -0300 Subject: [PATCH 62/67] feat(src/pages/proposals): add total and active proposals count --- src/pages/proposals/index.tsx | 13 +++++++++++++ src/services/CacheService.ts | 1 - src/utils/cache.ts | 8 +++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/pages/proposals/index.tsx b/src/pages/proposals/index.tsx index 600aa11ab7..b801a05801 100644 --- a/src/pages/proposals/index.tsx +++ b/src/pages/proposals/index.tsx @@ -21,6 +21,7 @@ import { formatNumberValue, PendingAction, isVoteNo, + VotingMachineProposalState, } from '../../utils'; import { FiFeather, @@ -71,6 +72,12 @@ const ProposalsPage = observer(() => { setSchemesFilter, } = useFilteredProposals(); + const allProposals = daoStore.getAllProposals(); + const activeProposalsCount = allProposals.filter( + proposal => + proposal.stateInVotingMachine > VotingMachineProposalState.Executed + ).length; + const history = useHistory(); return ( @@ -84,6 +91,12 @@ const ProposalsPage = observer(() => { + + {allProposals.length} Total Proposals + + + {activeProposalsCount} Active Proposals + diff --git a/src/services/CacheService.ts b/src/services/CacheService.ts index 72e432f4d3..412437186e 100644 --- a/src/services/CacheService.ts +++ b/src/services/CacheService.ts @@ -1439,7 +1439,6 @@ export default class UtilsService { 10000000 ) : []; - if ( votingMachineExecutionEvent.length > 0 && votingMachineExecutionEvent[0].data !== diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 33f7374fe1..2785a2500c 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -131,7 +131,13 @@ export const getRawEvents = async function ( from = fromBlock; while (from < to) { console.debug( - `Fetching logs of ${contractAddress} from blocks ${from} -> ${to}` + `Fetching logs of ${contractAddress} from blocks ${from} -> ${to}`, + { + address: contractAddress, + fromBlock: from, + toBlock: to, + topics: topicsToGet, + } ); try { let eventsFetched = await web3.eth.getPastLogs({ From 2d92a3c90b3adbcdd13d6f6b0d540130cc9f5864 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 18 May 2022 10:49:13 -0300 Subject: [PATCH 63/67] fix(scripts): changes to work with latest deploy-dxdao-contracts harhdat task --- hardhat.config.js | 6 +- package.json | 2 +- scripts/dev.ts | 341 +++++++++++++++++++++++++--------------------- yarn.lock | 12 +- 4 files changed, 194 insertions(+), 167 deletions(-) diff --git a/hardhat.config.js b/hardhat.config.js index dc22195a4b..44641c3a01 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -1,7 +1,7 @@ const moment = require('moment'); require('@nomiclabs/hardhat-truffle5'); require('hardhat-dependency-compiler'); -require('./node_modules/dxdao-contracts/scripts/deploy-dxvote'); +require('./node_modules/dxdao-contracts/scripts/deploy-dxdao-contracts'); require('@typechain/hardhat'); const MNEMONIC = @@ -21,8 +21,8 @@ module.exports = { 'dxdao-contracts/contracts/dxdao/DxToken.sol', 'dxdao-contracts/contracts/dxvote/DXDVotingMachine.sol', 'dxdao-contracts/contracts/dxvote/WalletScheme.sol', - 'dxdao-contracts/contracts/dxvote/utils/DXDVestingFactory.sol', - 'dxdao-contracts/contracts/dxvote/utils/DXdaoNFT.sol', + 'dxdao-contracts/contracts/dxvote/utils/ERC20VestingFactory.sol', + 'dxdao-contracts/contracts/dxvote/utils/ERC721Factory.sol', 'dxdao-contracts/contracts/utils/PermissionRegistry.sol', 'dxdao-contracts/contracts/utils/Multicall.sol', 'dxdao-contracts/contracts/test/ERC20Mock.sol', diff --git a/package.json b/package.json index c94bec01c5..596878f769 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "bignumber.js": "^9.0.2", "content-hash": "^2.5.2", "copy-to-clipboard": "^3.3.1", - "dxdao-contracts": "https://github.com/DXGovernance/dxdao-contracts.git#develop", + "dxdao-contracts": "https://github.com/DXGovernance/dxdao-contracts.git#12ea7f4e1ea070699d148d8c4ced4aaa88f08627", "eslint-plugin-cypress": "^2.12.1", "ether-swr": "^2.0.1", "ethers": "^5.5.2", diff --git a/scripts/dev.ts b/scripts/dev.ts index 0332ff6858..41ba430914 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -17,25 +17,159 @@ async function main() { const accounts = await web3.eth.getAccounts(); const deployconfig = { - reputation: [ - { - address: accounts[0], - amount: 6000, - }, - { - address: accounts[1], - amount: 4000, - }, - { - address: accounts[2], - amount: 1000, + dao: { + reputation: [ + { + address: accounts[0], + amount: 6000, + }, + { + address: accounts[1], + amount: 4000, + }, + { + address: accounts[2], + amount: 1000, + }, + ], + contributionReward: { + queuedVoteRequiredPercentage: 50, + queuedVotePeriodLimit: moment.duration(10, 'minutes').asSeconds(), + boostedVotePeriodLimit: moment.duration(3, 'minutes').asSeconds(), + preBoostedVotePeriodLimit: moment.duration(1, 'minutes').asSeconds(), + thresholdConst: 2000, + quietEndingPeriod: moment.duration(0.5, 'minutes').asSeconds(), + proposingRepReward: 10, + votersReputationLossRatio: 100, + minimumDaoBounty: web3.utils.toWei('1'), + daoBountyConst: 100, }, - ], + + walletSchemes: [ + { + name: 'RegistrarWalletScheme', + doAvatarGenericCalls: true, + maxSecondsForExecution: moment.duration(31, 'days').asSeconds(), + maxRepPercentageChange: 0, + controllerPermissions: { + canGenericCall: true, + canUpgrade: true, + canRegisterSchemes: true, + }, + permissions: [], + queuedVoteRequiredPercentage: 75, + boostedVoteRequiredPercentage: 5 * 100, + queuedVotePeriodLimit: moment.duration(15, 'minutes').asSeconds(), + boostedVotePeriodLimit: moment.duration(5, 'minutes').asSeconds(), + preBoostedVotePeriodLimit: moment.duration(2, 'minutes').asSeconds(), + thresholdConst: 2000, + quietEndingPeriod: moment.duration(1, 'minutes').asSeconds(), + proposingRepReward: 0, + votersReputationLossRatio: 100, + minimumDaoBounty: web3.utils.toWei('10'), + daoBountyConst: 100, + }, + { + name: 'MasterWalletScheme', + doAvatarGenericCalls: true, + maxSecondsForExecution: moment.duration(31, 'days').asSeconds(), + maxRepPercentageChange: 40, + controllerPermissions: { + canGenericCall: true, + canUpgrade: false, + canChangeConstraints: false, + canRegisterSchemes: false, + }, + permissions: [ + { + asset: '0x0000000000000000000000000000000000000000', + to: 'DXDVotingMachine', + functionSignature: '0xaaaaaaaa', + value: + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + allowed: true, + }, + { + asset: '0x0000000000000000000000000000000000000000', + to: 'RegistrarWalletScheme', + functionSignature: '0xaaaaaaaa', + value: + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + allowed: true, + }, + { + asset: '0x0000000000000000000000000000000000000000', + to: 'ITSELF', + functionSignature: '0xaaaaaaaa', + value: + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + allowed: true, + }, + ], + queuedVoteRequiredPercentage: 50, + boostedVoteRequiredPercentage: 2 * 100, + queuedVotePeriodLimit: moment.duration(10, 'minutes').asSeconds(), + boostedVotePeriodLimit: moment.duration(3, 'minutes').asSeconds(), + preBoostedVotePeriodLimit: moment.duration(1, 'minutes').asSeconds(), + thresholdConst: 1500, + quietEndingPeriod: moment.duration(0.5, 'minutes').asSeconds(), + proposingRepReward: 0, + votersReputationLossRatio: 5, + minimumDaoBounty: web3.utils.toWei('1'), + daoBountyConst: 10, + }, + { + name: 'QuickWalletScheme', + doAvatarGenericCalls: false, + maxSecondsForExecution: moment.duration(31, 'days').asSeconds(), + maxRepPercentageChange: 1, + controllerPermissions: { + canGenericCall: false, + canUpgrade: false, + canChangeConstraints: false, + canRegisterSchemes: false, + }, + permissions: [ + { + asset: '0x0000000000000000000000000000000000000000', + to: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa', + functionSignature: '0xaaaaaaaa', + value: + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + allowed: true, + }, + { + asset: 'DXD', + to: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa', + functionSignature: '0xaaaaaaaa', + value: + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + allowed: true, + }, + ], + queuedVoteRequiredPercentage: 50, + boostedVoteRequiredPercentage: 10 * 100, + queuedVotePeriodLimit: moment.duration(5, 'minutes').asSeconds(), + boostedVotePeriodLimit: moment.duration(1, 'minutes').asSeconds(), + preBoostedVotePeriodLimit: moment + .duration(0.5, 'minutes') + .asSeconds(), + thresholdConst: 1300, + quietEndingPeriod: moment.duration(0.5, 'minutes').asSeconds(), + proposingRepReward: 0, + votersReputationLossRatio: 10, + minimumDaoBounty: web3.utils.toWei('0.1'), + daoBountyConst: 10, + }, + ], + }, tokens: [ { name: 'DXDao on localhost', symbol: 'DXD', + type: 'ERC20', + decimals: 18, distribution: [ { address: accounts[0], @@ -54,6 +188,8 @@ async function main() { { name: 'REPGuildToken', symbol: 'RGT', + type: 'ERC20SnapshotRep', + decimals: 18, distribution: [ { address: accounts[0], @@ -72,6 +208,8 @@ async function main() { { name: 'Snapshot Guild Token', symbol: 'SGT', + type: 'ERC20', + decimals: 18, distribution: [ { address: accounts[0], @@ -89,137 +227,11 @@ async function main() { }, ], - permissionRegistryDelay: moment.duration(10, 'minutes').asSeconds(), - - contributionReward: { - queuedVoteRequiredPercentage: 50, - queuedVotePeriodLimit: moment.duration(10, 'minutes').asSeconds(), - boostedVotePeriodLimit: moment.duration(3, 'minutes').asSeconds(), - preBoostedVotePeriodLimit: moment.duration(1, 'minutes').asSeconds(), - thresholdConst: 2000, - quietEndingPeriod: moment.duration(0.5, 'minutes').asSeconds(), - proposingRepReward: 10, - votersReputationLossRatio: 100, - minimumDaoBounty: web3.utils.toWei('1'), - daoBountyConst: 100, + guildRegistry: { + address: ZERO_ADDRESS, + owner: 'Avatar', }, - walletSchemes: [ - { - name: 'RegistrarWalletScheme', - doAvatarGenericCalls: true, - maxSecondsForExecution: moment.duration(31, 'days').asSeconds(), - maxRepPercentageChange: 0, - controllerPermissions: { - canGenericCall: true, - canUpgrade: true, - canRegisterSchemes: true, - }, - permissions: [], - queuedVoteRequiredPercentage: 75, - boostedVoteRequiredPercentage: 5 * 100, - queuedVotePeriodLimit: moment.duration(15, 'minutes').asSeconds(), - boostedVotePeriodLimit: moment.duration(5, 'minutes').asSeconds(), - preBoostedVotePeriodLimit: moment.duration(2, 'minutes').asSeconds(), - thresholdConst: 2000, - quietEndingPeriod: moment.duration(1, 'minutes').asSeconds(), - proposingRepReward: 0, - votersReputationLossRatio: 100, - minimumDaoBounty: web3.utils.toWei('10'), - daoBountyConst: 100, - }, - { - name: 'MasterWalletScheme', - doAvatarGenericCalls: true, - maxSecondsForExecution: moment.duration(31, 'days').asSeconds(), - maxRepPercentageChange: 40, - controllerPermissions: { - canGenericCall: true, - canUpgrade: false, - canChangeConstraints: false, - canRegisterSchemes: false, - }, - permissions: [ - { - asset: '0x0000000000000000000000000000000000000000', - to: 'DXDVotingMachine', - functionSignature: '0xaaaaaaaa', - value: - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - allowed: true, - }, - { - asset: '0x0000000000000000000000000000000000000000', - to: 'RegistrarWalletScheme', - functionSignature: '0xaaaaaaaa', - value: - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - allowed: true, - }, - { - asset: '0x0000000000000000000000000000000000000000', - to: 'ITSELF', - functionSignature: '0xaaaaaaaa', - value: - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - allowed: true, - }, - ], - queuedVoteRequiredPercentage: 50, - boostedVoteRequiredPercentage: 2 * 100, - queuedVotePeriodLimit: moment.duration(10, 'minutes').asSeconds(), - boostedVotePeriodLimit: moment.duration(3, 'minutes').asSeconds(), - preBoostedVotePeriodLimit: moment.duration(1, 'minutes').asSeconds(), - thresholdConst: 1500, - quietEndingPeriod: moment.duration(0.5, 'minutes').asSeconds(), - proposingRepReward: 0, - votersReputationLossRatio: 5, - minimumDaoBounty: web3.utils.toWei('1'), - daoBountyConst: 10, - }, - { - name: 'QuickWalletScheme', - doAvatarGenericCalls: false, - maxSecondsForExecution: moment.duration(31, 'days').asSeconds(), - maxRepPercentageChange: 1, - controllerPermissions: { - canGenericCall: false, - canUpgrade: false, - canChangeConstraints: false, - canRegisterSchemes: false, - }, - permissions: [ - { - asset: '0x0000000000000000000000000000000000000000', - to: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa', - functionSignature: '0xaaaaaaaa', - value: - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - allowed: true, - }, - { - asset: 'DXD', - to: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa', - functionSignature: '0xaaaaaaaa', - value: - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - allowed: true, - }, - ], - queuedVoteRequiredPercentage: 50, - boostedVoteRequiredPercentage: 10 * 100, - queuedVotePeriodLimit: moment.duration(5, 'minutes').asSeconds(), - boostedVotePeriodLimit: moment.duration(1, 'minutes').asSeconds(), - preBoostedVotePeriodLimit: moment.duration(0.5, 'minutes').asSeconds(), - thresholdConst: 1300, - quietEndingPeriod: moment.duration(0.5, 'minutes').asSeconds(), - proposingRepReward: 0, - votersReputationLossRatio: 10, - minimumDaoBounty: web3.utils.toWei('0.1'), - daoBountyConst: 10, - }, - ], - guilds: [ { token: 'DXD', @@ -262,10 +274,9 @@ async function main() { }, ], - startTimestampForActions: moment().subtract(26, 'minutes').unix(), - actions: [ { + timestamp: moment().subtract(26, 'minutes').unix(), type: 'transfer', from: accounts[0], data: { @@ -356,6 +367,15 @@ async function main() { scheme: 'MasterWalletScheme', }, }, + { + type: 'approve', + from: accounts[2], + data: { + asset: 'DXD', + address: 'DXDVotingMachine', + amount: web3.utils.toWei('101'), + }, + }, { type: 'stake', from: accounts[2], @@ -367,7 +387,7 @@ async function main() { }, { type: 'vote', - time: moment.duration(1, 'minutes').asSeconds(), + increaseTime: moment.duration(1, 'minutes').asSeconds(), from: accounts[2], data: { proposal: '0', @@ -377,7 +397,7 @@ async function main() { }, { type: 'execute', - time: moment.duration(3, 'minutes').asSeconds(), + increaseTime: moment.duration(3, 'minutes').asSeconds(), from: accounts[2], data: { proposal: '0', @@ -404,6 +424,15 @@ async function main() { scheme: 'MasterWalletScheme', }, }, + { + type: 'approve', + from: accounts[2], + data: { + asset: 'DXD', + address: 'DXDVotingMachine', + amount: web3.utils.toWei('101'), + }, + }, { type: 'stake', from: accounts[2], @@ -415,7 +444,7 @@ async function main() { }, { type: 'vote', - time: moment.duration(1, 'minutes').asSeconds(), + increaseTime: moment.duration(1, 'minutes').asSeconds(), from: accounts[2], data: { proposal: '1', @@ -467,12 +496,12 @@ async function main() { }, { type: 'guild-withdrawTokens', + increaseTime: moment.duration(10, 'minutes').asSeconds() + 1, from: accounts[0], data: { guildName: 'DXDGuild', amount: web3.utils.toWei('10'), }, - time: moment.duration(10, 'minutes').asSeconds() + 1, }, { type: 'guild-createProposal', @@ -509,8 +538,8 @@ async function main() { }, }, { - time: moment.duration(10, 'minutes').asSeconds(), type: 'guild-endProposal', + increaseTime: moment.duration(10, 'minutes').asSeconds(), from: accounts[1], data: { guildName: 'DXDGuild', @@ -560,7 +589,7 @@ async function main() { ], }; - const { networkContracts, addresses } = await hre.run('deploy-dxvote', { + const networkContracts = await hre.run('deploy-dxdao-contracts', { deployconfig: JSON.stringify(deployconfig), }); @@ -619,7 +648,7 @@ async function main() { ], tokens: [ { - address: addresses.DXD, + address: networkContracts.addresses.DXD, name: 'DXdao on Localhost', decimals: 18, symbol: 'DXD', @@ -628,7 +657,7 @@ async function main() { 'https://s2.coinmarketcap.com/static/img/coins/200x200/5589.png', }, { - address: addresses.RGT, + address: networkContracts.addresses.RGT, name: 'REP Guild Token on Localhost', decimals: 18, symbol: 'RGT', @@ -637,7 +666,7 @@ async function main() { 'https://s2.coinmarketcap.com/static/img/coins/200x200/5589.png', }, { - address: addresses.SGT, + address: networkContracts.addresses.SGT, name: 'Snapshot Guild Token on Localhost', decimals: 18, symbol: 'SGT', @@ -646,7 +675,11 @@ async function main() { 'https://s2.coinmarketcap.com/static/img/coins/200x200/5589.png', }, ], - guilds: [addresses.DXDGuild, addresses.REPGuild, addresses.SnapshotGuild], + guilds: [ + networkContracts.addresses.DXDGuild, + networkContracts.addresses.REPGuild, + networkContracts.addresses.SnapshotGuild, + ], }; mkdirSync(path.resolve(__dirname, '../src/configs/localhost'), { diff --git a/yarn.lock b/yarn.lock index a2b1c67bbc..5a7f7c2e91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3277,11 +3277,6 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@realitio/realitio-contracts@>=0.0.0": - version "2.2.9" - resolved "https://registry.yarnpkg.com/@realitio/realitio-contracts/-/realitio-contracts-2.2.9.tgz#4274dd0ceca6d1b64a13553fe458312c920680d7" - integrity sha512-Ka4oMm6a7+W4kVcDjPa4Ov0pstd4nGk+2/rt8ShL6COwLFIgeybJmoZYBSCqLDrqfYk9N1glCjpBbU0EojTRTg== - "@resolver-engine/core@^0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.2.1.tgz#0d71803f6d3b8cb2e9ed481a1bf0ca5f5256d0c0" @@ -9093,15 +9088,14 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" -"dxdao-contracts@https://github.com/DXGovernance/dxdao-contracts.git#develop": - version "0.0.1" - resolved "https://github.com/DXGovernance/dxdao-contracts.git#fe74c569b0721a23e1975f714aa324181381bab3" +"dxdao-contracts@https://github.com/DXGovernance/dxdao-contracts.git#12ea7f4e1ea070699d148d8c4ced4aaa88f08627": + version "1.0.0" + resolved "https://github.com/DXGovernance/dxdao-contracts.git#12ea7f4e1ea070699d148d8c4ced4aaa88f08627" dependencies: "@maticnetwork/eth-decoder" "^0.0.4" "@openzeppelin/contracts" "4.4.0" "@openzeppelin/contracts-upgradeable" "4.4.0" "@openzeppelin/test-helpers" "^0.5.15" - "@realitio/realitio-contracts" ">=0.0.0" "@truffle/hdwallet-provider" "^1.4.0" arb-ethers-web3-bridge "^0.7.3" chai "^4.2.0" From 32d1ff263a3e7d61586a5d5b70b449cf3e83e2bd Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 18 May 2022 10:56:26 -0300 Subject: [PATCH 64/67] fix(hardhat): change seephrase to the same one used in dxdao-contracts --- hardhat.config.js | 39 ++++++++++++++++++++++++++++- scripts/dev.sh | 62 +---------------------------------------------- 2 files changed, 39 insertions(+), 62 deletions(-) diff --git a/hardhat.config.js b/hardhat.config.js index 44641c3a01..efd5ce05cf 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -5,7 +5,44 @@ require('./node_modules/dxdao-contracts/scripts/deploy-dxdao-contracts'); require('@typechain/hardhat'); const MNEMONIC = - 'dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao'; + 'cream core pear sure dinner indoor citizen divorce sudden captain subject remember'; + + +// # Accounts +// # ======== +// # Account #0: 0x9578e973bba0cc33bdbc93c7f77bb3fe6d47d68a (10000 ETH) +// # Private Key #0: 0x2edaf5755c340d57c68ab5c084a0afd867caafcbcf556838f404468e2ad0ea94 + +// # Account #1: 0xc5b20ade9c9cd5e0cc087c62b26b815a4bc1881f (10000 ETH) +// # Private Key #1: 0x40126ad770c1ff59937436ddab2872193c01d5353213d297fdb0ea2c13b5981e + +// # Account #2: 0xaf8eb8c3a5d9d900aa0b98e3df0bcc17d3c5f698 (10000 ETH) +// # Private Key #2: 0x4db6b61624bd4a9bf87ff59e7fca0991b02ff605664a3ad97dc237c84ba0e013 + +// # Account #3: 0x84eeb305da0a4309a696d43de9f79f04e66eb4f8 (10000 ETH) +// # Private Key #3: 0x6d8b1b46346a00fec52fd0e2edba75592e8814b11aec5815ec0f6b882e072131 + +// # Account #4: 0x1b929bdde0fb3b7b759696f23d6cac0963d326e6 (10000 ETH) +// # Private Key #4: 0x19ea21f217094f12da6bab83fe697f902caea0dcf5a2914d7c000b73938f7d85 + +// # Account #5: 0xd507743abcdb265f5fcef125e3f6cf7250cfe9da (10000 ETH) +// # Private Key #5: 0x6a944885ff4551fd546c59a2322a967af9906f596f60ecd110505c278f464f6e + +// # Account #6: 0x9af7a0d34fcf09a735ddaa03adc825398a6557ae (10000 ETH) +// # Private Key #6: 0x4299ee99407089bfc51e829734c0f6c1b366f515d5ddb5ece4f880a2f8fd430c + +// # Account #7: 0x2154cdc3632db21a2635819afa450f2dda08aebd (10000 ETH) +// # Private Key #7: 0x0e7ee7881e497062427ed392d310f09ca993fa964040c751cc383c10f55efc7c + +// # Account #8: 0x73c8825893ba6b197f883f60a20b4926c0f32a2c (10000 ETH) +// # Private Key #8: 0xd84954f2cea66fd01a872496f25ddb86db79ee81366609fbcff8087c9739b63a + +// # Account #9: 0x73d2888f96bc0eb530490ca5342d0c274d95654d (10000 ETH) +// # Private Key #9: 0xd20a2f6a6656d291ca4c4e6121b479db81b3b281e64707ff4a068acf549dc03c + +// # Account #10: 0xf8a3681248934f1139be67e0c22a6af450eb9d7c (10000 ETH) +// # Private Key #10: 0x8188d555d06262bfa3a343fa809b59b6368f02aa5a1ac5a3d2cb24e18e2b556e + const INFURA_API_KEY = process.env.REACT_APP_KEY_INFURA_API_KEY; const ALCHEMY_API_KEY = process.env.REACT_APP_KEY_ALCHEMY_API_KEY || ''; diff --git a/scripts/dev.sh b/scripts/dev.sh index caf451f745..31429b2661 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -12,7 +12,7 @@ cleanup() { kill -9 $hardhat_pid fi } -mnemonic="dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao dxdao" +mnemonic="cream core pear sure dinner indoor citizen divorce sudden captain subject remember" hardhat_running() { nc -z localhost 8545 @@ -23,66 +23,6 @@ start-hardhat_node() { yarn hardhat compile npx hardhat node > /dev/null & - - # Account #0: 0x79706c8e413cdaee9e63f282507287b9ea9c0928 (10000 ETH) - # Private Key: 0xe408e147b1335674887c1ac7dc3c45de9762aa824cf6255fd8bd61fecf15f021 - # - # Account #1: 0xc73480525e9d1198d448ece4a01daea851f72a9d (10000 ETH) - # Private Key: 0x6c8a6a9a7dbad13d6b41089648ae4b7971116611e4acd8f052c478dd8c62673e - # - # Account #2: 0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351 (10000 ETH) - # Private Key: 0x0054b824c2083e7db09f36edb2ab24eb31f8276fa6cd62e30b42e3a185b37179 - # - # Account #3: 0xaf1a6415202453d79b84d8a9d055b8f9141f696b (10000 ETH) - # Private Key: 0x3688ff0d0a95dd8b90bc68739505b488ff4908291eeb36380a94227d22653ce3 - # - # Account #4: 0x02803e2cdff171d1910d178dac948c711937bd3f (10000 ETH) - # Private Key: 0x530caa05cf058253ed14a2e6ccc5dcb952f09c7bcdcfb4be37e572e85dcafd1e - # - # Account #5: 0x797c62953ef866a1ece855c4033cc5dc3c11290b (10000 ETH) - # Private Key: 0x88ae5259286213d051e99da743d79be5d69dc75ee317bc887f5996ff004b83a6 - # - # Account #6: 0x016f70459e4ba98e0d60a5f5855e117e8ff39cae (10000 ETH) - # Private Key: 0x68f5bc4b52a67b3d800d0d8832ae3b89067a3bbee68c66544147e312d996d994 - # - # Account #7: 0x073f4fdc12f805b8d98e58551fc10d0a71bbc7db (10000 ETH) - # Private Key: 0x9adc9dfdce8383a1634716bf7a2e317a145f37a176a7b42538ace5ac20e223a1 - # - # Account #8: 0x6829556f30899d70403947590ffe8900e8c0d0d7 (10000 ETH) - # Private Key: 0x13436bc37e24487c2f1739d1ce6b8271a8465fee93aa3685ce543e56a50e1692 - # - # Account #9: 0x2b410bcb3b8096164fa8c6a06220a66bfb77058d (10000 ETH) - # Private Key: 0x4fe097bbfe75d9531d253b7f917f89dcee6664d832e40a8d82d717602dfeeb6c - # - # Account #10: 0x309f75c54a57937a7a0c6eb9b36eb1dbca82407e (10000 ETH) - # Private Key: 0xb10da35e4abe181d1143aa28a7da6c5f303660b954cf283accfeba2dfb56ab51 - # - # Account #11: 0xec9d2d34ad6acda19ab8afe71595051b206e3e4d (10000 ETH) - # Private Key: 0xfdf0c66289fafea1803c64522d073d6cc9ec71ba76e945a7c207f1f5ebb8e3b1 - # - # Account #12: 0x40c23c536bad1fe206ce911114f2c70309a7e487 (10000 ETH) - # Private Key: 0x97c63b257e8f86e05ae5a7bbb025b02d179b8d00fb9fbcdbfcdf04dcf9173cf2 - # - # Account #13: 0x28d254f2ddb522c43a21d338e337fd8d2f820db2 (10000 ETH) - # Private Key: 0xcdef57c095755d77bbbb327a187e67039c62fe39425e29b3646d334f54d28808 - # - # Account #14: 0xaf7386ce842cc0cffef91361059b0ca9ae48d6a0 (10000 ETH) - # Private Key: 0x4739bf3390cd5be10d0f58d2c1e887a186b544af563fa62717a6c324b36fed59 - # - # Account #15: 0x46c18451aaead6a2cb888b5bd6193c0f2c402329 (10000 ETH) - # Private Key: 0xc6b5889c8fbd0f3304ddd53b85f056a32b8338f99e5b8877ecb1d1c5543c8d6a - # - # Account #16: 0xc707c8143a6e1274ae7f637946f685870925261f (10000 ETH) - # Private Key: 0x4b00e0c8e17e88d588b204121594f14d20d1abe50e280d599ff39d6b35c44533 - # - # Account #17: 0x5b14a88dbbb04abcb6e5bf6384491be8d939cf57 (10000 ETH) - # Private Key: 0x18eecce45e3211ce6ce967f66c404798e36e8298b4b5222ebf597b841ebd868a - # - # Account #18: 0x92d356240dda25d050aa441690b92b2fa0011b84 (10000 ETH) - # Private Key: 0xe53525f97971b006e14820a8a7b74f8aae375b6635735d89b4db2e4cbdf0e8e0 - # - # Account #19: 0x5a485c203d9537095a6be2acc5a7ad83805d301d (10000 ETH) - # Private Key: 0xb86f3287c11a77c7317c2484be2bd386816876ead8ceaf86971b7b7c1afbb12b hardhat_pid=$! From 2b50adbbb965d520012403d95b4220707031b3e9 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 23 May 2022 13:36:23 -0300 Subject: [PATCH 65/67] fix(src/providers): use alchemyapi as defualt rpc mainet --- src/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 9bfa25902f..38888c7db9 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -39,7 +39,7 @@ export const NETWORKS: ChainConfig[] = id: 1, name: 'mainnet', displayName: 'Ethereum Mainnet', - defaultRpc: POKT_NETWORK_URLS['1'], + defaultRpc: `https://eth-mainnet.alchemyapi.io/v2/${defaultAlchemyKey}`, nativeAsset: { name: 'Ethereum', symbol: 'ETH', From 3b43392ff469fdbb973ccde9172d0f6364d181dd Mon Sep 17 00:00:00 2001 From: AugustoL Date: Mon, 23 May 2022 17:33:05 -0300 Subject: [PATCH 66/67] chore(cache): update cache for mainnet, gnosis chain and arbitrum --- src/configs/default.json | 4 +- src/configs/mainnet/config.json | 4 +- src/configs/proposalTitles.json | 188 +++++++++++++++++++++++++++++++- src/configs/xdai/config.json | 4 +- 4 files changed, 193 insertions(+), 7 deletions(-) diff --git a/src/configs/default.json b/src/configs/default.json index 7c767589ce..a7967d6975 100644 --- a/src/configs/default.json +++ b/src/configs/default.json @@ -1,6 +1,6 @@ { - "mainnet": "QmXW4x43LumaPZao1WrdZcGcCsR43Pfmqui9dxywr8QAxc", - "xdai": "QmW4AaiGvx19damvBVK7jn9WrCia7QV9RMdDpB2hW6JLrk", + "mainnet": "QmfLMqd4Skt8v89u5NuhYGzDbdQsfWPeM9gSTwzg1za46p", + "xdai": "QmTXxeSJCTgkBg9eYrBkHQqFfMFfJN6yrKqLUKodkSBDSt", "arbitrum": "QmQTAtR3nteZpGAHjArUk9Zu9F3UnXVmWgccXrVzzE6PGB", "rinkeby": "QmSq3xW8cL9yPzqVJyJLKsiRaxmAKNnQ5H4zXCZDxLynZG", "arbitrumTestnet": "QmUuwGiETkoytzoABzBS37LHqBPqMYTDEFHVkio8VrQepm" diff --git a/src/configs/mainnet/config.json b/src/configs/mainnet/config.json index f7c01e76a3..22be8495df 100644 --- a/src/configs/mainnet/config.json +++ b/src/configs/mainnet/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 7219912, - "ipfsHash": "QmUVcBbXqyWkNJorCTxpsaB6L12GhPSF9TwucyDrAgnsdC", - "toBlock": "14609184" + "ipfsHash": "QmSiUzqh2W8EvBJWJ5BUQbzBZiYwN5JsotrZ6RjEueMeJD", + "toBlock": "14831037" }, "contracts": { "avatar": "0x519b70055af55a007110b4ff99b0ea33071c720a", diff --git a/src/configs/proposalTitles.json b/src/configs/proposalTitles.json index bf4e0bccc2..9611fca8f2 100644 --- a/src/configs/proposalTitles.json +++ b/src/configs/proposalTitles.json @@ -1601,5 +1601,191 @@ "0xf8bb4742b850198584c7bee39437151f5a6f6987dd23c106080f437002d2a155": "[DRAFT] Levotiate Contributor Proposal 11/03/2022 - 11/05/2022", "0xf9d1887b1308e51583c8064da44b5487817b36811368dc0fe9fe78454ed5e740": "CRV / WETH Epoch 12 - 13 SWPR farming campaign", "0xfb1795ba0a2b4ee12007fabc5f1847f344b40fbce8476727a648814e2e802261": "WETH / WBTC Epoch 12 - 13 SWPR farming campaign", - "0xff32174f3f2ff80955d5075937f72d07f5bc18364946cc2f54bc57c5f42a29ec": "xDXD Buyback Order #8" + "0xff32174f3f2ff80955d5075937f72d07f5bc18364946cc2f54bc57c5f42a29ec": "xDXD Buyback Order #8", + "0x21ced878e0a12ea293711f6ac88546a730bba9a35f4dc9347c0a589562329797": "Stake 2k ETH to StakeWise - Signal Proposal", + "0x3e8416ee56beef52e28ea49ebe2839db3e620e8d3c695cd9c9f19b83f79bff40": "Decentral Labs Worker Proposal - 18th March to 18th May (2nd Payment)", + "0x53c9666338692a720a3ee3841b49f58a4d580cf7ed11acb2d43a484c8d09bc88": "Transfer of Funds to GC Base 500k", + "0x545f71ed3ef6e9efbe9340f634bb480bafa50639edeb5df7b86e4061b9fcf726": "xDXD Buyback Order #155 for 13.46 WETH", + "0x556cc36c8b7f807ad9770df79199f53462b8f93c6060023d97ccb3efc532c38e": "Payout to Sigma Prime for Auditing Services", + "0x5b1fb2e66669ad69531309fcfe342f4a1bfd995eb6940de049a625a4948289a9": "Decentral Labs Worker Proposal - 18th March to 18th May (2nd Payment)", + "0x636be996d018fc346bcce52e41c1026d41cf5cfb6665418334606d19c6cd7625": "Claim Mainnet REP for SkyMine Labs - Nov-Dec 2021", + "0x69a881265303706f921c9c1ad25de4b0635ff288893455b34f18cdaf9e7f86de": "DXD Buyback Program Adjustments, Extension #8 and Transfer of 373 ETH", + "0x79fb75b821690a15eb00764020bdb32d08704a5d340ff32eeb46543ff1caadb3": "Update dxstats.eth content hash to DXstats v1.5.0", + "0x7cc440cc8f6026c15213c4c551721f85d00d3a0908915331d454a9a326bd23f1": "Madusha - Mainnet REP Sync", + "0x880d07de4907b66d72521faec4de98283b92e9beb093b8ff7cdcb6112363b334": "Caney Fork - ETH Denver Stipend + GEN reimbursement", + "0x9a1c901b918f40f48a9cc1344a7556526952d6b697a02fdb479bd1ded5ab7e39": "Movement of Funds to Gnosis Chain DAI", + "0x9c62869ae0347e7f65e18733e4bdf9f155e9d43b14250295757e88520e6a7043": "Movement of Funds to Gnosis Chain ETH", + "0xd2114f86cd669d2a34b529e822024d4b280f7427639f0399106c4ae81997a2d8": "DXD Buyback Extension #7 - 385 ETH", + "0xe41bca110d96136684a2e916521ea9201f5a3f273aeaa518ca479f36961285f4": "Update swapr.eth content hash to Swapr v1.0.0 Beta 12", + "0xec89d8b01cc2ff1c77e501d056f2c584fcf323b10488583ccfeaeba90a06fdc8": "(Venky) Sync REP from Gnosis chain to Mainnet", + "0x00d4d73fd9cf48d95c5456dbd9b367236128e92359d600d2b09ce4cc2cb95bd4": "Leonardo Berteotti Contributor Proposal [02/02/2022 - 18/02/2022]", + "0x020b7aa9daca82035281fedd3bcfb42b56a67122b5ff53301b2c34c4e1d4bfdc": "SkyMine Labs - Stipend and Reimbursement around ETH Week Amsterdam", + "0x027d890c43faf6a00c691e65bd57e0375fa29c60b6629a24906614db3f3954ba": "DXD Buyback Program Adjustments, Extension #8 and Transfer of 373 ETH [Mirror Proposal]", + "0x03633066a51aaa63db6da76fbb07f8835bad46d43f1f58e6805c937a94bafdf2": "xDXD Buyback Order #159 for 12.59 WETH", + "0x0437d812f9581c992c12cf015252ced24dcd475d34e0e41256cdb6abee45eb8a": "Zett Proposal April, May, June 2022, Payout Period #1 | Product Owner & UI/UX", + "0x06338668a6d047c452722c3cc5bf2aea39daec9e9f92a1c3b2338fbf0e9c9c74": "xDXD Buyback Order #180 for 11.04 WETH", + "0x0c86b647fc9f921b2d7594e80fcf9bb904a5cb09e526d8c423797f863c46d0a8": "DXdao Discord Q1 2022 REP Boost for Sarah", + "0x0dcf78fae64e3f5d105768fb4fbcf4fa021504c7254bd97966b3b81c59e85d5c": "xDXD Buyback Order #165 for 10.08 WETH", + "0x0df20cd1df98288189770f51c1ca09dfdb798a7701e0dca02b35b3dc6076fc97": "Member Balancer to Supply xDXdao with 10,000 xGEN for Continued Governance Operations", + "0x122e8aaf69ac6c2eb0b8576a01baa7f0b0d72775ac100e04fd111515686a182f": "Venky - DXAmsterdam - stipend", + "0x13f6f59e9684d54de3f27a16061f10a59f723d9fea5135510d6d3217d474102c": "xDXD Buyback Order #169 for 9.91 WETH", + "0x1b7634ab666c2a29bc85d830921c8c367a559b4c8bdcfa79b3e78e582f294e8c": "Adam Azad - Contributor Proposal March - April 2022 (late) - March payout", + "0x1cc9c1bc8f168029744113be1ea4205faf53a17983bfab722a42b72b3cf67457": "xDXD Buyback Order #181 for 10.81 WETH", + "0x1d7f784feb2ab279ce228be60672add5aab82aaf701762f4188f625efa8a15ee": "Signal Proposal: DXdao to commit to Ministry position in Dawn DAO organized by The DAOist", + "0x1ef0ceb197194b356d30b1acac18f4c6571563579f07e9bd2b122b68f3e10b48": "xDXD Buyback Order #166 for 10.17 WETH", + "0x1fe7d4a7baa0ccc75b3640d3be0abda448afcd6754f20b6cfe755a45b87e6aa4": "AllyQ Contributor Proposal 1/24/2022-3/18/2022 2/2", + "0x23ec0ba69003e9c8d0724a2b368c1d6d6a151e1f5a5673604f1097de28e966c6": "VanGrim (through Zanarkand AB) Contributor Proposal (12 May - 11 July 2022)", + "0x2497b482da5188cbd71cbc90dcceb1f3077142c55dab87f3107f4d3af31b9c1a": "CRV / WETH Epoch 16 - 17 SWPR farming campaign", + "0x269918338f757a7e4c56de96acf1e517282cab63f97485dc5ce38b8b6e374ede": "AllyQ Contributor Proposal 5/23/2022-7/15/2022 1/2", + "0x28e4d114cad1d9ef56d0e43953d44b7dee4786714b6d6429410e57bcf9f04d65": "AugustoL Contributor Work Proposal #4 Payout", + "0x2a8121413822a157f678e719ccdacbbf6a3faea752485c77685506aacc7feace": "Space Inch Development Work - April 2022", + "0x2afdb83c932c74bd5b39030a38c411c3afb8784a166dfb6bec0d2a4b7ee7212b": "xDXD Buyback Order #163 for 11.2 WETH", + "0x2f020d2fbb83b52923da248af446caa642de28af37d6f2a106e38bfdf050e162": "xDXD Buyback Order #194 for 13.99 WETH", + "0x328d33e198b9c4a882c647fdb1ea1469db89d631492260daecbc5bbde6956ea9": "xDXD Buyback Order #193 for 13.07 WETH", + "0x3453fe5a59e7c62ce400a8bba2c03d1b7f0792e513cc690ef3446bb2f9a17f2a": "xDXD Buyback Order #167 for 10.11 WETH", + "0x3495509fdd80bc85acbd43808c999fa67de371861579da4fad502f7d32b722ad": "Dino Crescimbeni - Contributor proposal [16/05/2022 - 15/08/2022]", + "0x34c95d7652e11caffc1dac7b885d0f2be28710b9012acbb651ce27bfefa45a04": "xDXD Buyback Order #189 for 11.97 WETH", + "0x36453048f17d2a6612da9380ee7526aba69976cf13637688b3beb6e701ff9cfb": "Melanie Contributor Proposal 2/1/2022-3/31/2022 - End", + "0x37feff80c278ccd50524730c81841678c7dc4710957626cfb25af517a22c8328": "xDXD Buyback Order #191 for 11.44 WETH", + "0x399c05f720af6db7dbb2ce7c539c3c48fdcefebbfeb2fc3a12ec65dba645249b": "Borisblock — Contributor Proposal [07/03/22 - 07/05/22] 2/2", + "0x3a8e9d0b5909b8dbaa1bc391c7f708dfa1432afa98a10e86a1197457d9037733": "SkyMine Labs - Contributor Proposal 5/22 to 6/22", + "0x3ad287fad8243f2ab939f0b5dee1b019ba8aeb80247f3719faf7ec87c6a7c153": "Nathan, beginning of new period 05/01/22 - 06/25/22", + "0x3c207c2cb2005382f4ee55b11949057df74e2914c3f66f172014736ea1a3207f": "0xKLOM.eth Contributor Proposal for 05/2022 to 06/2022 1/2", + "0x3c26e5af14f6c524ef8c8f32bb19fdbc86777acba84e70390cff89bb1e32e747": "xDXD Buyback Order #195 for 14.23 WETH", + "0x3d4de42fbdb25ede9391ab6c5e87985d524f7cd42f47c75fd75acb1e581b2cf1": "Wixzi Contributor Proposal [05/04/2022 - 18/06/2022]", + "0x3ea8a6cbf6c9f7c8087271473e098af2f16747f6e07408a1b5a6fab72580f7d4": "DXdao Discord Q1 2022 REP Boost for squidz", + "0x3fe2e55c745fba7e7f9ad5ee91e2bfd79ca13f4557cf2725247047b1c0a90849": "Madusha - Contributor Proposal [01/01/2022 - 01/03/2022] - Second Half", + "0x3fe35e33bfe91259ac39a0573d99f77042a695bbf5740c2c3c1d2549d3c3085f": "Space Inch Development Work in March 2022", + "0x41dc8a5d63f83d88227697b86954ae2d03891bc8996b592ffaa0897c4f9af82e": "xDXD Buyback Order #197 for 16.5 WETH", + "0x42984b06d80ce009f37621bad8bc85001497f6f3907562e9ff27f72f334a2c33": "DXdao Discord Q1 2022 REP Boost for TheThriller", + "0x42c0a5c0edd7b817c7d6e31e57c70ef3da70795ddf764fd4ac49b5dc7a6081e8": "xDXD Buyback Order #175 for 10.63 WETH", + "0x42d379b00f25079dd7964ebbc8c1fe6d44e03f1bd340aa68442c12df568cd909": "xDXD Buyback Order #185 for 11.62 WETH", + "0x4525007fb5948688f94c8db0644ca55ea5d3543ddedb8c8eeb3bfb954e982c60": "Space Inch Development Work in February 2022", + "0x45cdf73bb23f462aba74c8714b44784c7148bc953d59369dbe9c9fd6a65e5961": "xDXD Buyback Order #174 for 10.76 WETH", + "0x4a70402e1e43f472a7b58f52969390cd77cc7896cc8a9bb492f87303dc1c2e68": "Guerrap Contributor Proposal [14/03/2022 - 01/06/2022] [1/2]", + "0x4ad40975ba5bff960bdb7dfa1f6a7973ba84f4a70bd15e3b9313e48a075b2f2c": "xDXD Buyback Order #200 for 11.47 WETH", + "0x4b394e097059ecb4c3163c4e952f933ae8124072d19c3777b923087bef347fef": "Unwrap 65k WXDAI to Dai", + "0x4c48923b379c4910191f27c80dcd7148af588ca648f9304d3e7731f8f83f0b04": "(REDO) - Member Balancer to Supply xDXdao with 10,000 xGEN for Continued Governance Operations", + "0x4c87360f51d53256be331f459bdcebd659e2368d30ca9441c8b84cfe1bc38304": "Adam Azad - Contributor Proposal and DAI payout - Jan - Feb 2022", + "0x5031bca4a8f68ba19fdf6f2d302ba69b7eec3d5761c4f3420b391b035eda9ac6": "xDXD Buyback Order #184 for 11.18 WETH", + "0x5061ddc2fd7542b5119b3e6f6c651c17ef561f1b1e92d1023590b080ccc99784": "AGVE / XDAI Epoch 16 - 17 SWPR farming campaign", + "0x524dfaf6404411a9cab6635a99198ce127dcd1d398a0fd58f1432eb8802f225e": "xDXD Buyback Order #182 for 10.87 WETH", + "0x52a7e5f06b36a916e88e0e6c0adbc3fc8fb2b7e23970a0c5fcf81b41669a50b3": " Melanie Contributor Proposal 4/1/22 - 5/31/22", + "0x54e907e00b8897f6d67a8214bf735f84d655ca83f58783c2ec1639d4c64fd453": "Proposal: Carrot Awareness Activation During ETH week in Amsterdam (April 18-25, 2022)", + "0x55b61b9fea9a4f77fb6e4728437f1a8a306416f6cefe0067029eaa50efe90813": "Wayne’s Worker Proposal - Trial Period Late Jan 2022 to Early April 2022", + "0x55ff34c97949e067ed11379a5ebf2c032b146fd83b11b03d48e8685fde618761": "Wayne's Worker Proposal - Early April 2022 - Late May 2022 (signal)", + "0x5715b4637b8e32cf3f44dac8fbd78f38a87fc1563e5957e4eecbdd5723a03450": "Wayne - DXAmsterdam Contributor Stipend", + "0x57a0c98453a71eb8e745aa2bc1d3bf66a110cd2aeae9cbdfa62d750406b10766": "DXdao Discord Q1 2022 REP Boost for SpicySoup", + "0x57fd56a03e35d0b72b50bb594508352d7de0d5e3e42da36802916ac0573baf5e": "DXdao Discord Q1 2022 REP Boost for Tom-Fr", + "0x586578ee4810f82e5d9c7dbe43e430a56584924b9a0d99e23e3be1c4077a5c88": "xDXD Buyback Order #151 for 13.42 WETH", + "0x591e0bb6b2c5efa5b97f81d70784353ffc15a692ba58edca648bc147013c7511": "xDXD Buyback Order #156 for 13.93 WETH", + "0x594b2166099be1b5c99958b74c3fe46e5bfbf53020222b7134886d78287cfd7d": "xDXD Buyback Order #150 for 13.19 WETH", + "0x5cf0c076fc5cb1d7931cfbcb2988c9302494b5b967f87caa04cc01c4deee484a": "Carl B - DXamsterdam stipend", + "0x5e1617ca4b3b6d1ebed7c576eaaf428241e6a77c3407b578af82f4c71002c6bf": "Signal Proposal for DXdao Discord REP Boosts Q1 2022", + "0x610e201c8ac544589cd9d2743e3463344fe4d43065abe6726211eb59d53c4032": "SkyMine Labs - 1/22 to 2/22 Contribution Recap", + "0x615ab3d3e492672d555b01eecca68023ca5e8f1c014bcda6da5e4e4d79aea0df": "xDXD Buyback Order #173 for 10.75 WETH", + "0x62e0b3b82715547de7090f628f4c3bda4322226d664ef4e9f41a5cc4e8d7ab78": "December 2021", + "0x62f0b6b36f2071c93ed3b4b6cfcce354dadf9caf31fbfea64d95809195bc967f": "Diogo Ferreira Contributor Proposal [4/04/2022 - 15/04/2022]", + "0x62fb4da3c53ae8c70710acf49f9771a7f7b84ec36ce502a65dc2fd57f592bfc5": "DXColombia Contributor Stipend", + "0x651ae49a00ea204017b1dfb7ca7430bae1fad61a184ebbdb1b6ebc13b35f3ce0": "Worker proposal - 26/01/2022 to 25/03/2022 - luzzifoss", + "0x653fa2503a9d3fa394f234ec69e217270e12c0ace69880e4fb9f18509eb9303d": "DXdao Discord Q1 2022 REP Boost for Chiminiv9", + "0x6914b20e6c054d8787c6acb13ef1b61c5587a4912e2b7ced90f36b9aa09820e4": "xDXD Buyback Order #164 for 10.33 WETH", + "0x698c40183fd31e9df74d0a7d391c62b1475cd53b5cec730aa9090262876a79f0": "Caney Fork Feb/Mar 2022 Proposal (2/2) + DXAmsterdam stipend", + "0x6c2366402f1baa13661d68659ca4981c28c11078b67f9c6a10389a0d1e3fc922": "​ Borisblock — Contributor Proposal [07/03/22 - 07/05/22] 1/2", + "0x6cf3ce433d7d90f4f66486a9e09be8540bf0840e76a593b8e10f8a658897cf04": "xDXD Buyback Order #196 for 16.49 WETH", + "0x6da5d04d5cb90b4cf62cb609d6bf71524dcc0630e67063ee3746bf0c1e50977c": "xDXD Buyback Order #148 for 11.65 WETH", + "0x6f8961cbd277969e9d149d1220d26b678f5e7193dbdff9f136239c54c67c53b4": "VanGrim Contributor proposal 12 March - 11 May (2nd part)", + "0x728980d7a2086a78561c2b24b82a04957a2d31fb41e84373de77d85b423d93a2": "Jorge Lopes Contributor Proposal [18 April 2022 - 18 May 2022]", + "0x74cb0a9436ddd5810bb7dac8e537def758d2be703abcb6a93cc7125bf52c8266": "Caney Fork Proposal & Payout Dec '21 & Jan '22", + "0x753bfb5a08d34e80f87f31fa697f00f991d2e77e54d0509e49feb76539846e0b": "0xKLOM.eth Contributor Proposal for 03/2022 to 04/2022 1/2", + "0x775a93f01e24feb1707be6c9df8df89cb3e10fb2286c36c77409f0b4dfcdf668": "VanGrim (through Zanarkand AB) - DXAmsterdam Contributor Stipend", + "0x776ecd66631b9a0d97247c3300617450c56bbbe090746e3ddaab817a0f3cde4a": "xDXD Buyback Order #144 for 14.05 WETH", + "0x78da1d7655fc15a9d10eb4f6a434c9c2144fe44661296d3a6b7182e09eeb6e47": "xDXD Buyback Order #158 for 12.57 WETH", + "0x797e661cad50dfc6956c19e3e2538b3f36aed16e29d17f0a1d34cb90384ed9d0": "Leonardo Berteotti Contributor Proposal [21/02/2022 - 15/04/2022]", + "0x79a64e3beb29b46eb7c9dd192e13d8ac405b5a94e8b04392900497ba511ab2bc": "Dlabs Worker Proposal 23rd May 23rd June", + "0x7ac00c7b3e2b4c80084bba14ef50f75784a545f1b092a7247fb02ad46707632e": "xDXD Buyback Order #199 for 10.98 WETH", + "0x7c3b8569d7d196f5c2764bb4f7edf1e0c6fcdca10f1be153b8adb5dda4663609": "0xKLOM.eth Contributor Proposal for 03/2022 to 04/2022 2/2", + "0x7d706e5797f06b58d11fb7924856b55b0399fb4788447f23421cfdab16905f6e": "Violet Worker Proposal 31/03/2022-31/05/2022 1/2", + "0x7dd1a6275b358c040e113a069c88d3f44e4782ba1138cb8c30be2670e26d4b8a": "Tammy Vested DXD Proposal from periods 12.20-5.21", + "0x7e6b833973c06433dd53c04ed7664798fb11aae83881346d442c60279b087b71": "Carl B Contributor Proposal [2022-02-21 - 2022-05-09]", + "0x7fb7e060c4b6442e2cbd4fbd48bde7005c8553c9fa10f27d8d266793593fab6b": "SkyMine Labs - 3/22 to 4/22 Contribution Recap & Payment", + "0x817730e1c57f948c587a19b54177ab23ef7332a04c73986e1b66a06a5fc5983d": "xDXD Buyback Order #188 for 11.92 WETH", + "0x872704d2f1619dd5d83b9ca255348647b504322aac54bc4783712d6f6883020f": "Etherlabs AB - payment proposal - April 2022", + "0x8893d52df36f581cf2e30a17c5459380db55799d31fce85dd5e9b17e20a3251a": "Milton Contributor Proposal [04/04/2022 - 29/05/2022] (1/2)", + "0x88b8b7e41ea978cd9dad9fc9939ea56177813663d2d696ebe8a34bf147a1d17d": "MAI / XDAI Epoch 16 - 17 SWPR farming campaign", + "0x89fff26d1ddd066908be3072b78aad19225c79c78ab3753ce168113dd3be26a9": "xDXD Buyback Order #161 for 12.17 WETH", + "0x8bfde4ea88ff76a420a7cac5617cc500ac78c274a8c7787b5f5542562d0fb702": "xDXD Buyback Order #149 for 12.08 WETH", + "0x8e496a70764ca712c6444df8486024e5d88b98b97a0688c6ea2a325384e019c1": " Carl B Contributor Proposal [2022-02-21 - 2022-05-09]", + "0x9230fa1b8a5d6643c029a9259256e2d852c3a485b31694b9e0e33645ceeedbca": "AllyQ Contributor Proposal 3/28/2022-5/20/2022 2/2", + "0x929c071b90372dc4346cc7480290d00636dae64ba566562a8022afbcbd99477c": "xDXD Buyback Order #155 for 13.46 WETH", + "0x981f7ad155bb4d79fe963426b22d26bde961aa069b3afb6924968ac0325e1b08": "DAI payout (26/12/2021-25/01/2022) - luzzifoss", + "0x9897941fba1f7450d3c038868485ac4ca23b83cc695eb9326f9394e6a6ded43f": "Stake 255 GNO - Signal Proposal", + "0x991b4570a439520098f00d23755bce9e58a28d58ece5c54d2e4148b2d49fe5c8": "RICE / XDAI Epoch 16 - 17 SWPR farming campaign", + "0x9a9a8bd064b947fc9bb32f71b7813aee6fdbea0abe9cd620204f0d4b3731b979": "xDXD Buyback Order #147 for 11.83 WETH", + "0x9c05f686011ebb52a19955e2ffe63ba8ee0505dd29365b3a6a2490f2ab3ff714": "xDXD Buyback Order #146 for 13.99 WETH", + "0x9f5faacc7f41665b447e1423f3d79274a8434a7e7f2b4a2dd71b85d2146fb36c": "xDXD Buyback Order #171 for 9.79 WETH", + "0x9f66d49e0b800dca0de8d21044f7cdf67af8e12154d01b785253e29419a2eab2": "Nathan, proposal at end of period 3/5/22-4/30/22", + "0xa194a4b2ae0050d2396e030709e10b85daa13e6ba1d4f0642d2573e45c961e9d": "xDXD Buyback Order #154 for 13.66 WETH", + "0xa2be4abc07207c4a273cd977428078b3d81292522cd170d4e0f9c95091568427": "AllyQ Contributor Proposal 3/28/2022-5/20/2022 1/2", + "0xa59d47177919ce1121216ab29e6e1b15d0d1f5ebd238d8eda8c5ba54d6def171": "xDXD Buyback Order #190 for 11.36 WETH", + "0xa7523735a2f01afd953d4e7650c7323f2b3714d4b7b81133e471e734206f676a": "xDXD Buyback Order #162 for 11.9 WETH", + "0xa793418ec15a861296ddad02148bd28a6078e799ea605d299b7dd9c38656e5cb": "0xKLOM.eth Contributor Proposal for 01/2022 to 03/2022 2/2", + "0xa7eed37b53104a70aed9d73e811498ea0fee9a9e634230fd5745d94ebb3f7d4e": "xDXD Buyback Order #197 for 10.64 WETH", + "0xad91395f9ae6453c66819806237380cb9231812b2784ac727ad14aed5c348fe6": "xDXD Buyback Order #157 for 13.65 WETH", + "0xb0f303cda263824463740d66e310d06cf1a4f73bea67a6a8a51d95c37bcf532a": "​Borisblock — Contributor Proposal [07/05/22 - 07/07/22] 1/2", + "0xb399905e93fcc1ea604cc965a2622b39a62318706965aa13319784ad20a069f1": "Ross Neilson Worker Proposal 18/4/2022-18/6/2022 1/2", + "0xb7804c3f333e5012a66be5027f0ee73c9d765b54bb086f836ac20a905a46e20e": "xDXD Buyback Order #168 for 10.53 WETH", + "0xb7eb202304dbf0b1fa2fda84a1b972ac8ebc7c2dff79661de8de67c5e4ee2b2a": "Diogo - Contributor Proposal [16/04/2022 - 16/06/2022]", + "0xb7fc6d125eed2595e715c4f857a6512748d2bc96bd4a1adbce9ff21f209152ab": "xDXD Buyback Order #192 for 12.44 WETH", + "0xbac6b0fed6d4e10445d27f03ebc424082bf4c6c5cf127ac2f89cac04e99671e9": "Madusha - Contributor Proposal [01/03/2022 - 30/04/2022] - Second Half", + "0xbc80c062bb326a303dfc363083471eb8d6bf4146a8b6ba02328906ddb4ee16b5": "Adam Azad - REP Sync Proposal for January 1st to April 30 2022", + "0xc0cfd98a9317b65cd4b5496247b36bdf8990cf72b570e1b5d1f53c2ce7b5bdd2": "DXdao Discord Q1 2022 REP Boost for Cantillon", + "0xc51721575e515e3028ecb37ea148926a9ae9738f81c91564c435ffbc3437a009": "xDXD Buyback Order #183 for 11.72 WETH", + "0xcba3e751dece5607735455eed9e19271f1ae108ffbdbf8edc88d64d1281d75fc": "AllyQ Contributor Proposal 3/28/2022-5/20/2022 2/2", + "0xcbba59861ced43a022b2bf0bb5e26151b2e1088c7b0198d63b414732f6cf0aea": "Ross Neilson Worker Proposal 18/2/2022-18/4/2022 2/2", + "0xce7c3c0de28ba9b1d1c4180234200a4da01c4c1a900e39fd9671f1ff5cdaa71a": "Adam - Stipend for Devconnect Amsterdam", + "0xd066e809f19e7fcba92fd4c99001be124525484f5d37725c21d64479a248cd08": "Milan V Contributor Proposal [14/3/2022 - 14/5/2022] - Second Half", + "0xd2784e9d5a06ecf3db3cbf49147a5fb80d329d0d2fbb450274e77a7dabd08bb3": "xDXD Buyback Order #176 for 11.21 WETH", + "0xd39d6107149aafeb24a41a252a93a0a034609585059a82ffe395a99df5030bcd": "xDXD Buyback Order #152 for 13.76 WETH", + "0xd41c033b50c0d32cbcd5d8a15a9ad528de8732cc6542f0aff6100b61e5893dac": "Milton Contributor Proposal [07/02/2022 - 03/04/2022]", + "0xd59ed2baa46fc4b325a434e7c86922cee46801f2003b53f17b090cad1ea725d5": "Etherlabs AB - payment proposal - April 2022 (REP and vesting)", + "0xd91c3ef93c3fde9be42d2cf4d9d44aab1e98aff1c21a077fe3f47fe8e73c1e42": "xDXD Buyback Order #145 for 14.95 WETH", + "0xd98e432717bd3cef5046d3b5db806133b8ac79878cf83ccecd4b809ac1154f2f": "xDXD Buyback Order #143 for 14.99 WETH", + "0xd9fdc37c352bd29e87198f5463aa4c5ae3144887124ae245db2715f7cea14e8b": "xDXD Buyback Order #186 for 11.47 WETH", + "0xe03d013efeca08f6d0949861290bb2e8332d5dfe35f9516674ef5c208d84a91c": "Leonardo Berteotti Contributor Proposal [21/02/2022 - 15/04/2022] [END]", + "0xe3209990432c23293332367fbe0a399ede709e42c1c9640c72d949819c1185f0": "Wayne’s Worker Proposal - Trial Period Late Jan 2022 to Early April 2022", + "0xe4a0e623a08639960a2936b6eb629a24f48578e2f520cfe32b25af84f70826c3": "Milan V Contributor Proposal [14/3/2022 - 14/5/2022]", + "0xe71fa634850a4a10c9eb2610c39d680c7dd216cc76ce9b22d3ff6136c72fa9f4": "xDXD Buyback Order #160 for 12.21 WETH", + "0xe7eb6799d28cd42071dd73930b997d1979734d1cac61c83b0bd186136f03d5b8": "xDXD Buyback Order #177 for 10.86 WETH", + "0xeb093a056d71e391917e06a2751e0bd23cd7ef06092b1db4e4c8adf88642b8b7": "SkyMine Labs - Contributor Proposal 3/22 to 4/22", + "0xebdbce1b34db36d21ef282f356ae4778fbfcb5501b494ba505d3b6be312ba191": "HAUS / WETH Epoch 16 - 17 SWPR farming campaign", + "0xec1661e91a40e16976d06c8240559f21cc92cd58c127763bc74747f7192404a8": "Int_blue - Contributor Proposal - April - May (Trial Period)", + "0xec9f0b6961fff3c12f7fa80745997311de28135cbb3a2e3c0798eb9c82fffc64": "DXdao Discord Q1 2022 REP Boost for Tom-FR", + "0xed3af1bfdbb6e6fef16a925c7117d173c311ba8ad53bd7bfde53a09f01b02f68": "Caney Fork Worker Proposal (Feb payout)", + "0xed8b701a21d8df816253ee7718c233efe3dcbdd0a48ccd1f003fcd0ed9685498": "DXdao Discord Q1 2022 REP Boost for snufkin#7489", + "0xee7d2df08b9870157259690aac89380acec49fc4587178481338e31c97428f30": "Leonardo Berteotti Contributor Proposal [18/04/2022 - 10/06/2022]", + "0xeed6b0b0d4364a7186cfb30269e601fffecb57dbd4a9a63a435178ad46ef8692": "xDXD Buyback Order #172 for 10.29 WETH", + "0xf1e9f9b94b4998a2eccd70505d17c5f340965dbad018a3da7dc26463efba0724": "Melanie DXAmsterdam Contributor Stipend + Swag", + "0xf2a22d0253b8d8fe8c9a5141437c153d3f1219a46bb52db2ff616e917a0afbc4": "xDXD Buyback Order #143 for 12.65 WETH", + "0xf361b3de6f65d49f95f274330698297c1f0f4fbeac230a1affc3dd739bfc2bec": "Send 15.4 WETH to GP Relayer for xDXD buyback #4", + "0xf37c8f2d3b91c335f17b32267b37ad95d4fb76123fb65f32bef87947067276d1": "Etherlabs AB - payment proposal for Jan and Feb 2022 - stables", + "0xf386d0a218f489df4649b345c9e248369e82685b23dffb895f8a4bfe2d9f268b": "Zett Amsterdam Stipend, Copenhagen, Merch & Other Costs", + "0xf637871828ab1b20f7551cba7f81cc211502468f2273e77f619073c49d5ea6ef": "DAI payout (26/11/2021-26/12/2021) - luzzifoss", + "0xf64ef8e41d77cd00750b7cf0f82d1d5dd6a72ff9589c8c0667490e9318dd15eb": "Violet Worker Proposal 31/03/2022-31/05/2022 2/2 + Amsterdam stipend", + "0xf7a30ab1df563e6327e7a1114ea16161e1baf3aee6d07c62d8bccdc7b26f97a2": "Send 16.4 WETH to GP Relayer for xDXD buyback #11", + "0xf844a99ab8cb5556dc19b3dc76dc55ce88f3ed63f1aae6604318a5ced2e8e80c": "xDXD Buyback Order #178 for 11.06 WETH", + "0xfa21c90ad70a9f2f804246f3746b5aef5036864f29158c298efe49a28d6578f3": "Caney Fork Worker Proposal Apr/May 2022 (1/2)", + "0xfa8db9098d406b2efb145366102a314fc852a40d3c4361f4a30f15783bbf639a": "Violet Worker Proposal 31/01/2022-31/03/2022 1/2", + "0xfb27a478ec614dea90f5e0b4b4caa828fded254d0d589b120a119e0ca097ebad": "DAI payout Zett Period March 2022", + "0xfc921ce24f2458a725f2928d82e82888839af71a3cc15a8a29c374a3cae9102d": "Trial Worker Proposal - Adam Azad - January 13-27th, 2021", + "0xfcdb52fff5bed18bbce359b3bdeb5ffe0431c0e29f60af08b85aef969cf7fe25": "Melanie - ETHDenver Stipend Request", + "0xfd10e5ac9711008d583f7c8d5da9aec232be4f3ee9b64c715df95ad8340c6ba5": "xDXD Buyback Order #189 for 11.8 WETH", + "0xfe0d7e6f7c22c11e19a32d40f7360ce0251f2122d4a2a9a114bbd1d90853ff8d": "Melanie Worker Proposal 2/1/2022-3/31/2022", + "0xfe2765f0bbd28a9d00e6c231d12e19235b4f471b02fea652bc6b5d5280069a4f": "Nathan, contributor proposal for end of 01/08/22-3/4/22", + "0xfe8dbe253824d26a023cfbe75ccfd01a15defa01fd7f2712fb2a33a2bcb6b1bd": "DXdao Hackathon & Retreat Colombia October 2022", + "0xfeb2d79cd1e17a3e3f1b22b15a3b86d6284a1415dea8d120df0fb0ae1bb19f77": "xDXD Buyback Order #153 for 12.88 WETH", + "0xfeea9d463a997be8e640ecfdc7b8179e7424ebe8c0d8201c65c2019a7d7baf67": "Worker Proposal 06/21-07/21 - Kaden Zipfel Software", + "0x17dcc4df80b1dcde1c7943ecb9aacab9f6d369177f9dffdcc95be231511da1be": "Cross chain Member Balancer to Supply xDXdao with 35,000 xGEN for Continued Governance Operations", + "0x52c90416b88701ea09a44b0bd9bed5c7ea6db5cece2d28cb1456a7da668afc16": "Caney Fork Worker Proposal Feb/Mar 2022 (1/2)", + "0xa8036392ff2da5486d287473a8f1490fe74461e1838c098920dedca73ed0ecdf": "xDXD Buyback Order #179 for 11.01 WETH" } \ No newline at end of file diff --git a/src/configs/xdai/config.json b/src/configs/xdai/config.json index 1668b9bebe..a61839cefe 100644 --- a/src/configs/xdai/config.json +++ b/src/configs/xdai/config.json @@ -1,8 +1,8 @@ { "cache": { "fromBlock": 13060713, - "ipfsHash": "Qma5RVGFQiD9KhnQK1ZuVavT88zM7vSor68CzKiajsr9d7", - "toBlock": "21703404" + "ipfsHash": "QmQgax9dCsQbWwovbLG6NA1A8Z1pfoh5kRq1asqYZ8ReTM", + "toBlock": "22297636" }, "contracts": { "avatar": "0xe716EC63C5673B3a4732D22909b38d779fa47c3F", From d108515fd850cc00a12037da7919fdaba2ca0fcc Mon Sep 17 00:00:00 2001 From: Augusto Lemble Date: Wed, 25 May 2022 15:52:43 -0300 Subject: [PATCH 67/67] Set default RPC for production mainnet --- src/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 38888c7db9..ca1f59fcc7 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -116,7 +116,7 @@ export const NETWORKS: ChainConfig[] = id: 1, name: 'mainnet', displayName: 'Ethereum Mainnet', - defaultRpc: POKT_NETWORK_URLS['1'], + defaultRpc: `https://eth-mainnet.alchemyapi.io/v2/${defaultAlchemyKey}`, nativeAsset: { name: 'Ethereum', symbol: 'ETH',