Skip to content

Commit

Permalink
stakers can vote
Browse files Browse the repository at this point in the history
  • Loading branch information
Sorizen committed Apr 4, 2024
1 parent c91332a commit d92b23b
Show file tree
Hide file tree
Showing 16 changed files with 115 additions and 32 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning].

## [1.4.0-rc.0] - 2024-04-04
### Added
- Stakers also can vote for proposal

## [1.3.0] - 2024-03-27
### Changed
- `@rarimo/client` to support amino signer
Expand Down Expand Up @@ -95,7 +99,8 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic

- Initiated project

[Unreleased]: https://gitlab.com/rarimo/scan/compare/1.3.0...HEAD
[Unreleased]: https://gitlab.com/rarimo/scan/compare/1.4.0-rc.0...HEAD
[1.4.0-rc.0]: https://gitlab.com/rarimo/scan/compare/1.3.0...1.4.0-rc.0
[1.3.0]: https://gitlab.com/rarimo/scan/compare/1.2.1...1.3.0
[1.2.1]: https://gitlab.com/rarimo/scan/compare/1.2.0...1.2.1
[1.2.0]: https://gitlab.com/rarimo/scan/compare/1.1.4...1.2.0
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/package.json ./package.json
COPY .env .
COPY .env.local .


EXPOSE 8000
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ docker build -t scan .
Run the Docker image

```bash
docker run -p 8095:8095 --env-file .env -it scan
docker run -p 8095:8095 --env-file .env.local -it scan
```

## License
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "scan",
"version": "1.3.0",
"version": "1.4.0-rc.0",
"private": true,
"gitHooks": {
"pre-commit": "yarn lint",
Expand Down
3 changes: 3 additions & 0 deletions scripts/release-sanity-check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ function getVersion() {

const refsReport = exec('git log -1 --format="%D"').toString()
const versionMatch = refsReport.match(/tag: ([\w\d\-_.]+)/i)

console.log(refsReport)

return versionMatch ? versionMatch[1] : ''
}

Expand Down
31 changes: 21 additions & 10 deletions src/components/Forms/VoteForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const VOTER_LABEL_ID = 'proposal-voter-label'
enum VoteFormFieldNames {
Option = 'option',
Voter = 'voter',
Staker = 'staker',
}

export default function VoteForm({
Expand All @@ -44,10 +45,11 @@ export default function VoteForm({
proposalId,
}: FormProps & { proposalId: number; grants: GrantAuthorization[] }) {
const t = useI18n()
const { address, isValidator } = useWeb3()
const { address, isValidator, isStaker } = useWeb3()
const { localizeProposalVoteOption } = useLocalize()

const DEFAULT_VALUES = {
[VoteFormFieldNames.Staker]: isStaker,
[VoteFormFieldNames.Option]: VoteOption.Yes,
[VoteFormFieldNames.Voter]: isValidator ? address : grants?.[0]?.granter,
}
Expand All @@ -61,10 +63,21 @@ export default function VoteForm({
enableForm,
getErrorMessage,
} = useForm(DEFAULT_VALUES, yup =>
yup.object({
[VoteFormFieldNames.Option]: yup.number().required(),
[VoteFormFieldNames.Voter]: yup.string().required(),
}),
yup
.object({
[VoteFormFieldNames.Staker]: yup.boolean(),
[VoteFormFieldNames.Option]: yup
.number()
.when([VoteFormFieldNames.Staker], ([isStaker], schema) =>
isStaker ? schema.notRequired() : schema.required(),
),
[VoteFormFieldNames.Voter]: yup
.string()
.when([VoteFormFieldNames.Staker], ([isStaker], schema) =>
isStaker ? schema.notRequired() : schema.required(),
),
})
.omit([VoteFormFieldNames.Staker]),
)

const submit = async (formData: typeof DEFAULT_VALUES) => {
Expand All @@ -81,11 +94,9 @@ export default function VoteForm({
return
}

if (formData.voter !== address) {
await client.tx.execVoteProposal(address, formData.voter, proposalId, formData.option)
} else {
await client.tx.voteProposal(address, proposalId, formData[VoteFormFieldNames.Option])
}
formData.voter && formData.voter !== address
? await client.tx.execVoteProposal(address, formData.voter, proposalId, formData.option)
: await client.tx.voteProposal(address, proposalId, formData[VoteFormFieldNames.Option])

onSubmit({
message: t('vote-form.submitted-msg', {
Expand Down
27 changes: 16 additions & 11 deletions src/components/Proposal/Proposal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const VOTE_FORM_ID = 'vote-form'

export default function Proposal({ id }: { id: string }) {
const t = useI18n()
const { isConnected, isValidator, address } = useWeb3()
const { isConnected, isValidator, isStaker, address } = useWeb3()

const { data, isLoading, isLoadingError, reload } = useLoading<ProposalFragment>(
{} as ProposalFragment,
Expand Down Expand Up @@ -65,14 +65,24 @@ export default function Proposal({ id }: { id: string }) {
const tooltipText = useMemo(() => {
if (!isVotingAllowed) return t('proposal.vote-not-allowed-msg')
if (!isConnected) return t('proposal.connect-wallet-msg')
if (!isValidator && isGrantsEmpty) return t('proposal.not-validator-msg')
if (!isValidator && !isGrantsEmpty && !isStaker) return t('proposal.not-staker-msg')
if (!isValidator && isGrantsEmpty && !isStaker) return t('proposal.not-validator-msg')

return ''
}, [isValidator, isVotingAllowed, isGrantsEmpty, isConnected, t])
}, [isValidator, isVotingAllowed, isGrantsEmpty, isConnected, t, isStaker])

const isTooltipListenersDisable = useMemo(
() => (isValidator || !isGrantsEmpty) && isVotingAllowed && isConnected,
[isValidator, isGrantsEmpty, isVotingAllowed, isConnected],
() => (isValidator || !isGrantsEmpty || isStaker) && isVotingAllowed && isConnected,
[isValidator, isGrantsEmpty, isVotingAllowed, isConnected, isStaker],
)

const isVoteButtonDisabled = useMemo(
() =>
isDisabled ||
!isVotingAllowed ||
(!isValidator && isGrantsEmpty && !isStaker) ||
!isConnected,
[isDisabled, isVotingAllowed, isValidator, isGrantsEmpty, isConnected, isStaker],
)

const sectionAction = (
Expand All @@ -86,12 +96,7 @@ export default function Proposal({ id }: { id: string }) {
disableTouchListener={isTooltipListenersDisable}
>
<span>
<Button
onClick={openDialog}
disabled={
isDisabled || !isVotingAllowed || (!isValidator && isGrantsEmpty) || !isConnected
}
>
<Button onClick={openDialog} disabled={isVoteButtonDisabled}>
{t('proposal.vote-btn')}
</Button>
</span>
Expand Down
15 changes: 15 additions & 0 deletions src/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19069,6 +19069,13 @@ export type ValidatorFragment = { __typename?: 'validator', consensus_address: s

export type ValidatorBaseFragment = { __typename?: 'validator', validator_info?: { __typename?: 'validator_info', operator_address: string } | null, validator_commissions: Array<{ __typename?: 'validator_commission', commission: any }>, validator_signing_infos: Array<{ __typename?: 'validator_signing_info', missed_blocks_counter: any }>, validator_descriptions: Array<{ __typename?: 'validator_description', moniker?: string | null, avatar_url?: string | null }>, validator_statuses: Array<{ __typename?: 'validator_status', status: number, jailed: boolean }>, validator_voting_powers: Array<{ __typename?: 'validator_voting_power', voting_power: any }> };

export type GetAccountDelegationsQueryVariables = Exact<{
address: Scalars['String']['input'];
}>;


export type GetAccountDelegationsQuery = { __typename?: 'query_root', action_delegation?: { __typename?: 'ActionDelegationResponse', pagination?: any | null, delegations?: Array<any | null> | null } | null };

export type GetAccountValidatorInfosQueryVariables = Exact<{
address: Scalars['String']['input'];
}>;
Expand Down Expand Up @@ -19799,6 +19806,14 @@ export const ValidatorBase = gql`
}
}
`;
export const GetAccountDelegations = gql`
query GetAccountDelegations($address: String!) {
action_delegation(address: $address) {
pagination
delegations
}
}
`;
export const GetAccountValidatorInfos = gql`
query GetAccountValidatorInfos($address: String!) {
account(where: {address: {_eq: $address}}) {
Expand Down
5 changes: 5 additions & 0 deletions src/graphql/queries/GetAccountDelegations.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
query GetAccountDelegations($address: String!) {
action_delegation(address: $address) {
pagination
}
}
24 changes: 23 additions & 1 deletion src/hooks/useWeb3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@
import { useEffect, useState } from 'react'

import { getClient } from '@/client'
import { GetAccountValidatorInfos, GetAccountValidatorInfosQuery, getApollo } from '@/graphql'
import {
GetAccountDelegations,
GetAccountDelegationsQuery,
GetAccountValidatorInfos,
GetAccountValidatorInfosQuery,
getApollo,
} from '@/graphql'
import { ErrorHandler, isWindow } from '@/helpers'
import { useAppState } from '@/hooks'

export const useWeb3 = () => {
const {
isConnected,
isValidator,
isStaker,
address,
setIsConnected,
setAddress,
setIsValidator,
setIsStaker,
isInitialised,
} = useAppState()
const [isConnecting, setIsConnecting] = useState(false)
Expand All @@ -23,6 +31,17 @@ export const useWeb3 = () => {
isConnected,
isValidator,
address,
isStaker,
}

const getIsStaker = async (address: string) => {
const { data } = await getApollo().query<GetAccountDelegationsQuery>({
query: GetAccountDelegations,
fetchPolicy: 'network-only',
variables: { address },
})

return Boolean(data.action_delegation?.pagination.total)
}

const getIsValidator = async (address: string) => {
Expand All @@ -32,6 +51,7 @@ export const useWeb3 = () => {
variables: { address },
})

await getIsStaker(address)
return Boolean(data?.account?.[0]?.validator_infos?.length)
}

Expand All @@ -44,6 +64,7 @@ export const useWeb3 = () => {
setIsConnected(true)
setAddress(client.wallet.address)
setIsValidator(await getIsValidator(client.wallet.address))
setIsStaker(await getIsStaker(client.wallet.address))

await new Promise(resolve => {
if (state.address && state.isConnected) resolve(true)
Expand All @@ -57,6 +78,7 @@ export const useWeb3 = () => {

const disconnect = () => {
getClient().disconnect()
setIsStaker(false)
setIsValidator(false)
setAddress('')
setIsConnected(false)
Expand Down
10 changes: 7 additions & 3 deletions src/hooks/useWeb3Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const useWeb3Store = () => {
const [storage, setStorage] = useState<Web3Storage | null>(null)
const [isConnected, setIsConnected] = useState(false)
const [isValidator, setIsValidator] = useState(false)
const [isStaker, setIsStaker] = useState(false)
const [address, setAddress] = useState('')
const [isInitialised, setIsInitialised] = useState(false)

Expand All @@ -18,28 +19,31 @@ export const useWeb3Store = () => {
const _storage = Web3Storage.getInstance(() => window.localStorage)
setStorage(_storage)

const restored = _storage.getStorage({ isConnected, isValidator, address })
const restored = _storage.getStorage({ isConnected, isValidator, address, isStaker })

setIsConnected(restored.isConnected)
setIsValidator(restored.isValidator)
setIsStaker(isStaker)
setAddress(restored.address)
setIsInitialised(true)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isWindow()])

useEffect(() => {
if (!storage) return
storage.save({ isConnected, isValidator, address })
storage.save({ isConnected, isValidator, address, isStaker })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isConnected, isValidator, address, storage])
}, [isConnected, isValidator, address, storage, isStaker])

return {
isConnected,
isStaker,
isValidator,
isInitialised,
address,
setIsConnected,
setIsValidator,
setIsStaker,
setAddress,
}
}
1 change: 1 addition & 0 deletions src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ export default {
"title": "Proposal #{id} Details",
"proposal-vote-submitted-msg": "Vote for proposal #{id} successfully submitted.",
"connect-wallet-msg": "Please connect your wallet to be able to vote.",
"not-staker-msg": "Only users participating in staking can vote for proposals.",
"not-validator-msg": "Only validators allowed to vote for proposals.",
"vote-not-allowed-msg": "Not allowed proposal's status for voting.",
"vote-btn": "Vote",
Expand Down
6 changes: 6 additions & 0 deletions src/providers/app-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
isInitialised: isWeb3Initialised,
isConnected,
isValidator,
isStaker,
address,
setIsConnected,
setIsValidator,
setIsStaker,
setAddress,
} = useWeb3Store()

Expand All @@ -46,12 +48,14 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
themeMode,
isConnected,
isValidator,
isStaker,
address,
isMobileNavbarOpened,
isInitialised,
isSearchOpened,
setIsConnected,
setIsValidator,
setIsStaker,
setAddress,
setViewportWidth,
setThemeMode,
Expand All @@ -65,12 +69,14 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
themeMode,
isConnected,
isValidator,
isStaker,
address,
isMobileNavbarOpened,
isInitialised,
isSearchOpened,
setIsConnected,
setIsValidator,
setIsStaker,
setAddress,
setViewportWidth,
setThemeMode,
Expand Down
7 changes: 6 additions & 1 deletion src/storages/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export class Web3Storage extends BaseStorage<LocalStorageKeys.Web3> {
this.set(LocalStorageKeys.Web3, JSON.stringify(storage))
}

public getStorage(initialState: Web3StorageState): Web3StorageState {
public getStorage(initialState: {
address: string
isConnected: boolean
isStaker: boolean
isValidator: boolean
}): Web3StorageState {
const restored = this.get(LocalStorageKeys.Web3)
return restored ? JSON.parse(restored) : initialState
}
Expand Down
1 change: 1 addition & 0 deletions src/types/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type UiStorageState = {
}

export type Web3StorageState = {
isStaker: boolean
isConnected: boolean
isValidator: boolean
address: string
Expand Down
Loading

0 comments on commit d92b23b

Please sign in to comment.