Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blacklisted accounts #4683

Merged
merged 19 commits into from
Jan 3, 2024
Merged

Blacklisted accounts #4683

merged 19 commits into from
Jan 3, 2024

Conversation

vrrayz
Copy link
Contributor

@vrrayz vrrayz commented Dec 7, 2023

Fix for #4138

Copy link

vercel bot commented Dec 7, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
dao ✅ Ready (Inspect) Visit Preview Jan 3, 2024 3:36pm
pioneer-2 ✅ Ready (Inspect) Visit Preview Jan 3, 2024 3:36pm
pioneer-2-storybook ✅ Ready (Inspect) Visit Preview Jan 3, 2024 3:36pm

setAddresses(result.map((key: any) => encodeAddress(key.args[0])))
})
const accountsOptedOut = useFirstObservableValue(() => {
return api?.query.referendum?.accountsOptedOut.keys()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the skip should be kept because the chain shouldn't be queried every time useStakingAccountsLocks is called:

Suggested change
return api?.query.referendum?.accountsOptedOut.keys()
return skip ? undefined : api?.query.referendum?.accountsOptedOut.keys()

useEffect(() => {
if (!isVoting) return
if (!isVoting || !votingOptOutAccounts) return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So unless I'm missing something, I don't think this is needed:

Suggested change
if (!isVoting || !votingOptOutAccounts) return
if (!isVoting) return

Comment on lines 20 to 50
const mockDefaultBalance = {
locked: BN_ZERO,
recoverable: BN_ZERO,
transferable: BN_ZERO,
locks: [],
vestingTotal: BN_ZERO,
vestedClaimable: BN_ZERO,
vestedBalance: BN_ZERO,
vestingLocked: BN_ZERO,
vesting: [],
total: BN_THOUSAND,
}

const useMyBalances: AddressToBalanceMap = {
[allAccounts[0]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
[allAccounts[1]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
[allAccounts[2]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
[allAccounts[3]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed:

Suggested change
const mockDefaultBalance = {
locked: BN_ZERO,
recoverable: BN_ZERO,
transferable: BN_ZERO,
locks: [],
vestingTotal: BN_ZERO,
vestedClaimable: BN_ZERO,
vestedBalance: BN_ZERO,
vestingLocked: BN_ZERO,
vesting: [],
total: BN_THOUSAND,
}
const useMyBalances: AddressToBalanceMap = {
[allAccounts[0]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
[allAccounts[1]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
[allAccounts[2]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
[allAccounts[3]]: {
...mockDefaultBalance,
total: new BN(200_000_000_000),
},
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed:

After removing this and implementing other changes this error shows up
image

When writing the storybook test i came across the error earlier on and i figured mocking the locks that way would be the fix for it which was i used this. In toBalance the lockdetails are been used so i thought the storybook test needed such mock data
image

Comment on lines 13 to 18
const allAccounts = [
'j4Rc8VUXGYAx7FNbVZBFU72rQw3GaCuG2AkrUQWnWTh5SpemP',
'j4RjraznxDKae1aGL2L2xzXPSf8qCjFbjuw9sPWkoiy1UqWCa',
'j4RbTjvPyaufVVoxVGk5vEKHma1k7j5ZAQCaAL9qMKQWKAswW',
'j4Rh1cHtZFAQYGh7Y8RZwoXbkAPtZN46FmuYpKNiR3P2Dc2oz',
]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to use the account addresses directly, but the extensions are also supposed to provide a name so it's clearer IMO to just work on members (they all have a controller account which will be used in the mock:

Suggested change
const allAccounts = [
'j4Rc8VUXGYAx7FNbVZBFU72rQw3GaCuG2AkrUQWnWTh5SpemP',
'j4RjraznxDKae1aGL2L2xzXPSf8qCjFbjuw9sPWkoiy1UqWCa',
'j4RbTjvPyaufVVoxVGk5vEKHma1k7j5ZAQCaAL9qMKQWKAswW',
'j4Rh1cHtZFAQYGh7Y8RZwoXbkAPtZN46FmuYpKNiR3P2Dc2oz',
]
const allAccounts = [
{ member: member('alice'), balance: 20 },
{ member: member('bob'), balance: 20 },
{ member: member('charlie'), balance: 20 },
{ member: member('alice'), balance: 20 },
]

Finally here 20 should mean 20JOY not 20HAPI

},
referendum: {
accountsOptedOut: {
keys: [...allAccounts, ...allAccounts, ...allAccounts, ...allAccounts, ...allAccounts],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of addresses should be: allAccounts.map(({ member }) => member.controllerAccount).

So to trigger the pagination:

Suggested change
keys: [...allAccounts, ...allAccounts, ...allAccounts, ...allAccounts, ...allAccounts],
keys: Array.from({ length: 5 })
.fill(allAccounts.map(({ member }) => member.controllerAccount))
.flat(),

Comment on lines 80 to 88
const Template: Story = () => {
return (
<BalancesContext.Provider value={useMyBalances}>
<BlacklistedAccounts />
</BalancesContext.Provider>
)
}

export const Default = Template.bind({})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So no need for an additional provider:

Suggested change
const Template: Story = () => {
return (
<BalancesContext.Provider value={useMyBalances}>
<BlacklistedAccounts />
</BalancesContext.Provider>
)
}
export const Default = Template.bind({})
export const Default: Story = {}

},
}
export default {
title: 'Pages/Blacklisted Accounts',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally this should be under "Election":

Suggested change
title: 'Pages/Blacklisted Accounts',
title: 'Pages/Election/Blacklisted Accounts',

@@ -242,7 +242,7 @@ export const proposalsPagesChain = (
nextRewardPayments,
stage: { stage: { isIdle: true }, changedAt: 123 },
},
referendum: { stage: {} },
referendum: { stage: {}, accountsOptedOut: { keys: [] } },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So with the skip argument, this is not necessary

Suggested change
referendum: { stage: {}, accountsOptedOut: { keys: [] } },

Comment on lines 25 to 29
// const withComputed = (data: Record<any, any>) => Object.defineProperties(data, {
// unwrap: { value: () => data },
// isSome: { value: Object.keys(data).length > 0 },
// keys: { value: () => of(Object.keys(data).map(entry => ({ args: [entry] }))) },
// })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the commented code on this file.

You're right the value can't be returned from the asChainData, instead something similar could be done on asApiMethod but it's not urgent right now...

Copy link
Collaborator

@thesan thesan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it seems the balance encoding broke some tests. Please have a quick look. If there's no quick solution I'll fix it tomorrow.

Finally even if it works on storybook, this will require some actual QA before merging it. @ivanturlakov could you please create a bunch of accounts on Atlas dev transfer them some funds and Opt out of voting with them please ?
image

@@ -1,5 +1,6 @@
import { createType } from '@joystream/types'
import { mapValues } from 'lodash'
// import { of } from 'rxjs'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// import { of } from 'rxjs'

Comment on lines 111 to 123
const TotalBalance = ({ address, handleTotalBalance }: TotalBalanceProp) => {
const balance = useBalance(address)

const setTotalBalance = (number: BN) => {
handleTotalBalance(number)
}

useEffect(() => {
if (balance) setTotalBalance(balance.total)
}, [balance])

return <></>
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pattern to avoid. Instead please add a useBalances hook in packages/ui/src/accounts/hooks/useBalance.ts:

export const useBalances = (addresses: Address[]): Map<string, Balances | undefined> => {
  const { api } = useApi()
  const allMyBalances = useMyBalances()

  const [definedBalances, setDefinedBalances] = useState(new Map<string, Balances>())
  useEffect(() => {
    addresses.forEach((address) => {
      if (definedBalances.get(address)) return
      addressToBalances(api, allMyBalances, address).then((balances) => {
        if (balances) setDefinedBalances((entries) => new Map([...entries.entries(), [address, balances]]))
      })
    })
  }, [api, allMyBalances])

  return useMemo(() => new Map(addresses.map((address) => [address, definedBalances.get(address)])), [definedBalances])
}

const addressToBalances = async (
  api: Api | undefined,
  allMyBalances: AddressToBalanceMap | undefined,
  address: string
): Promise<Balances | undefined> => {
  if (!allMyBalances) return
  const myBalances = allMyBalances[address]
  if (myBalances) return myBalances

  if (!api) return
  return toBalances(await firstValueFrom(api.derive.balances.all(address)))
}

That was trickier to write than I thought and I haven't tested it yet. But IMO it's better than having fake components to loop over hooks.

Also with this hook the component should be simplified a bit.

@ivanturlakov
Copy link

ivanturlakov commented Jan 2, 2024

could you please create a bunch of accounts on Atlas dev transfer them some funds and Opt out of voting with them please ?

@thesan sure, what atlas instance should I use?

@thesan
Copy link
Collaborator

thesan commented Jan 3, 2024

@thesan sure, what atlas instance should I use?

@ivanturlakov I meant the Atlas dev network. However it seems that there is an opted out account already. So I guess nothing more is needed. Sorry I should have checked yesterday but I was convinced there was no opted out account on mainnet ATM.

@vrrayz please check: https://dao-git-fork-vrrayz-blacklisted-accounts-joystream.vercel.app/#/election/blacklisted-accounts there is a bug with the total counter, my guess is that it comes from: #4683 (comment) I'm surprised it didn't show on storybook (I guess the full app is triggering a lot more re-renders).

Comment on lines 16 to 17
export const BlacklistedAccount = ({ address }: Prop) => {
const balance = useBalance(address)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For most users useBalance will call derive.balances.all. RPC node queries are not cached and they should be reduced as much as possible. Since useAddresses is called on this page, this component shouldn't call useBalance. Instead:

Suggested change
export const BlacklistedAccount = ({ address }: Prop) => {
const balance = useBalance(address)
export const BlacklistedAccount = ({ address, balance }: Prop) => {

const ACCOUNTS_PER_PAGE = 18
const [page, setPage] = useState(1)
const votingOptOutAccounts = useVotingOptOutAccounts()
const [paginatedAccounts, setPaginatedAccounts] = useState<string[] | undefined>()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a result useBalances should be called on this component instead. Also I think the sum can be done here (no need for TotalBalance), and the paginated accounts should be { address: string, balance: BN | undefined }:

Suggested change
const [paginatedAccounts, setPaginatedAccounts] = useState<string[] | undefined>()
const balances = useBalances(addresses)
const totalBalance = useMemo(
() => balances.values().reduce((prev, value) => value?.total.add(prev) ?? prev, BN_ZERO),
[balances]
)
const paginatedAccounts = useMemo(
() =>
votingOptOutAccounts
.slice((page - 1) * ACCOUNTS_PER_PAGE, page * ACCOUNTS_PER_PAGE)
.map((address) => ({ address, balance: balance.get(address)?.total }))
[votingOptOutAccounts, page, balances])
)


if (myBalances) {
return myBalances
}

return balances ? toBalances(balances) : null
}
export const useBalances = (addresses: Address[]): Map<string, Balances | undefined> => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small detail but please skip a line here:

Suggested change
export const useBalances = (addresses: Address[]): Map<string, Balances | undefined> => {
export const useBalances = (addresses: Address[]): Map<string, Balances | undefined> => {

const [definedBalances, setDefinedBalances] = useState(new Map<string, Balances>())
useEffect(() => {
addresses.forEach((address) => {
if (definedBalances.get(address)) return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even smaller detail (and I know it's something I wrote) but:

Suggested change
if (definedBalances.get(address)) return
if (definedBalances.has(address)) return

Copy link
Collaborator

@thesan thesan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

I'm just waiting for #4713 to merge this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants