From ad895d8b41db8cf1a049b35db71d01faa3faec94 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:36:11 -0500 Subject: [PATCH 01/14] Add hatIdsToStreamIds to RolesStoreData and update interface methods --- src/store/roles/rolesStoreUtils.ts | 1 + src/store/roles/useRolesStore.ts | 3 +++ src/types/roles.tsx | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/store/roles/rolesStoreUtils.ts b/src/store/roles/rolesStoreUtils.ts index 5db97f48a0..33eb249e16 100644 --- a/src/store/roles/rolesStoreUtils.ts +++ b/src/store/roles/rolesStoreUtils.ts @@ -96,6 +96,7 @@ export const initialHatsStore: RolesStoreData = { decentHatsAddress: undefined, streamsFetched: false, contextChainId: null, + hatIdsToStreamIds: [], }; export const predictHatId = ({ adminHatId, hatsCount }: { adminHatId: Hex; hatsCount: number }) => { diff --git a/src/store/roles/useRolesStore.ts b/src/store/roles/useRolesStore.ts index 158e4baf7f..e986ddcfc5 100644 --- a/src/store/roles/useRolesStore.ts +++ b/src/store/roles/useRolesStore.ts @@ -129,6 +129,9 @@ const useRolesStore = create()((set, get) => ({ }, })); }, + setHatIdsToStreamIds: hatIdsToStreamIds => { + set(() => ({ hatIdsToStreamIds })); + }, resetHatsStore: () => set(() => initialHatsStore), })); diff --git a/src/types/roles.tsx b/src/types/roles.tsx index e1000b4777..5e7786987f 100644 --- a/src/types/roles.tsx +++ b/src/types/roles.tsx @@ -238,12 +238,14 @@ export interface RolesStoreData { hatsTree: undefined | null | DecentTree; streamsFetched: boolean; contextChainId: number | null; + hatIdsToStreamIds: { hatId: BigInt; streamId: string }[]; } export interface RolesStore extends RolesStoreData { getHat: (hatId: Hex) => DecentRoleHat | null; getPayment: (hatId: Hex, streamId: string) => SablierPayment | null; setHatsTreeId: (args: { contextChainId: number | null; hatsTreeId?: number | null }) => void; + setHatIdsToStreamIds: (hatIdsToStreamIds: { hatId: BigInt; streamId: string }[]) => void; setHatsTree: (params: { hatsTree: Tree | null | undefined; chainId: bigint; From 31cd3f6f37bf3f6ab94d691dc3aab1062d22a7df Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:36:48 -0500 Subject: [PATCH 02/14] Add getHatIdsToStreamIds function and integrate with useKeyValuePairs hook --- src/hooks/DAO/useKeyValuePairs.ts | 60 +++++++++++++++++++++++++++---- src/hooks/utils/useCreateRoles.ts | 2 ++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index 7a558a7f9b..dcec653fd9 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -2,7 +2,7 @@ import { abis } from '@fractal-framework/fractal-contracts'; import { hatIdToTreeId } from '@hatsprotocol/sdk-v1-core'; import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { GetContractEventsReturnType, getContract } from 'viem'; +import { Address, GetContractEventsReturnType, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import { logError } from '../../helpers/errorLogging'; import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider'; @@ -55,14 +55,50 @@ const getHatsTreeId = ( } }; +const getHatIdsToStreamIds = ( + events: GetContractEventsReturnType | undefined, + sablierV2LockupLinear: Address, + chainId: number, +) => { + if (!events) { + return []; + } + + const hatIdToStreamIdEvents = events.filter( + event => event.args.key && event.args.key === 'hatIdToStreamId', + ); + + const hatIdIdsToStreamIds = []; + for (const event of hatIdToStreamIdEvents) { + const hatIdToStreamId = event.args.value; + if (hatIdToStreamId !== undefined) { + const [hatId, streamId] = hatIdToStreamId.split(':'); + hatIdIdsToStreamIds.push({ + hatId: BigInt(hatId), + streamId: `${sablierV2LockupLinear.toLowerCase()}-${chainId}-${streamId}`, + }); + continue; + } + logError({ + message: "KVPairs 'hatIdToStreamId' without a value", + network: chainId, + args: { + transactionHash: event.transactionHash, + logIndex: event.logIndex, + }, + }); + } + return hatIdIdsToStreamIds; +}; + const useKeyValuePairs = () => { const publicClient = usePublicClient(); const node = useDaoInfoStore(); const { chain, - contracts: { keyValuePairs }, + contracts: { keyValuePairs, sablierV2LockupLinear }, } = useNetworkConfig(); - const { setHatsTreeId } = useRolesStore(); + const { setHatsTreeId, setHatIdsToStreamIds } = useRolesStore(); const [searchParams] = useSearchParams(); const safeAddress = node.safe?.address; @@ -81,12 +117,13 @@ const useKeyValuePairs = () => { }); keyValuePairsContract.getEvents .ValueUpdated({ theAddress: safeAddress }, { fromBlock: 0n }) - .then(safeEvents => + .then(safeEvents => { setHatsTreeId({ contextChainId: chain.id, hatsTreeId: getHatsTreeId(safeEvents, chain.id), - }), - ) + }); + setHatIdsToStreamIds(getHatIdsToStreamIds(safeEvents, sablierV2LockupLinear, chain.id)); + }) .catch(error => { setHatsTreeId({ hatsTreeId: null, contextChainId: chain.id }); logError(error); @@ -113,7 +150,16 @@ const useKeyValuePairs = () => { return () => { unwatch(); }; - }, [chain.id, keyValuePairs, safeAddress, publicClient, searchParams, setHatsTreeId]); + }, [ + chain.id, + keyValuePairs, + safeAddress, + publicClient, + searchParams, + setHatsTreeId, + setHatIdsToStreamIds, + sablierV2LockupLinear, + ]); }; export { useKeyValuePairs }; diff --git a/src/hooks/utils/useCreateRoles.ts b/src/hooks/utils/useCreateRoles.ts index f11124af65..7d0686010c 100644 --- a/src/hooks/utils/useCreateRoles.ts +++ b/src/hooks/utils/useCreateRoles.ts @@ -639,6 +639,7 @@ export default function useCreateRoles() { hats: [hatStruct], topHatId: BigInt(hatsTree.topHat.id), topHatAccount: hatsTree.topHat.smartAddress, + keyValuePairs, }, ], }); @@ -670,6 +671,7 @@ export default function useCreateRoles() { erc6551Registry, hatsAccount1ofNMasterCopy, hatsElectionsEligibilityMasterCopy, + keyValuePairs, ], ); From 2d0c14a53e38631834a0b52e49ee64f8e0a03151 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:37:16 -0500 Subject: [PATCH 03/14] Integrate hatIdsToStreamIds into useHatsTree for filtering payment streams --- src/hooks/DAO/loaders/useHatsTree.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index d7ce1bb568..a404fbfa3e 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -39,6 +39,7 @@ const useHatsTree = () => { setHatsTree, updateRolesWithStreams, resetHatsStore, + hatIdsToStreamIds, } = useRolesStore(); const ipfsClient = useIPFSClient(); @@ -271,12 +272,20 @@ const useHatsTree = () => { return hat; } const payments: SablierPayment[] = []; - // @todo - update Datepicker to choose more precise dates (Date.now()) if (hat.isTermed) { + const foundStreamIDs = hatIdsToStreamIds + .filter(item => item.hatId === BigInt(hat.id)) + .map(({ streamId }) => { + return streamId; + }); const recipients = hat.roleTerms.allTerms.map(term => term.nominee); const uniqueRecipients = [...new Set(recipients)]; for (const recipient of uniqueRecipients) { - payments.push(...(await getPaymentStreams(recipient))); + payments.push( + ...(await getPaymentStreams(recipient)).filter(stream => + foundStreamIDs.includes(stream.streamId), + ), + ); } } else { if (!hat.smartAddress) { @@ -294,7 +303,7 @@ const useHatsTree = () => { } getHatsStreams(); - }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched]); + }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched, hatIdsToStreamIds]); useEffect(() => { if (safeAddress && safe?.address && safeAddress !== safe.address && hatsTree) { From ec160ec5d4719fcbcc972cb342617d2436612420 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Thu, 21 Nov 2024 20:14:47 -0500 Subject: [PATCH 04/14] DatePicker is selected at the current 'time' --- src/components/ui/forms/DatePicker.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/ui/forms/DatePicker.tsx b/src/components/ui/forms/DatePicker.tsx index 0af223e1f3..f9155727a6 100644 --- a/src/components/ui/forms/DatePicker.tsx +++ b/src/components/ui/forms/DatePicker.tsx @@ -195,7 +195,15 @@ export function DatePicker({ const handleDateChange = (e: OnDateChangeValue) => { if (e instanceof Date) { - onChange?.(new Date(e.setHours(0, 0, 0, 0))); + // get the current time and then set selected date to it. + const currentHours = new Date().getHours(); + const currentMinutes = new Date().getMinutes(); + const currentSeconds = new Date().getSeconds(); + const currentMilliseconds = new Date().getMilliseconds(); + + onChange?.( + new Date(e.setHours(currentHours, currentMinutes, currentSeconds, currentMilliseconds)), + ); onClose(); // Close the menu after date selection } }; From e1f6b55d2229ee3de13a88a3c4676816f177453a Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:27:20 -0500 Subject: [PATCH 05/14] Update @fractal-framework/fractal-contracts to version 1.4.2 in package.json and package-lock.json --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7c7334453..97eca14f31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@fontsource/space-mono": "^5.0.19", - "@fractal-framework/fractal-contracts": "^1.4.1", + "@fractal-framework/fractal-contracts": "^1.4.2", "@graphprotocol/client-apollo": "^1.0.16", "@hatsprotocol/modules-sdk": "^1.4.0", "@hatsprotocol/sdk-v1-core": "^0.9.0", @@ -4991,9 +4991,9 @@ "integrity": "sha512-gz9yaKtXCY+HutNvQ4APc15xwZ1f6pWXve5N55x5m/hOoGqgB9Auf3l7CitHNhNJkSKEmaM45M29b0rFeudXlg==" }, "node_modules/@fractal-framework/fractal-contracts": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@fractal-framework/fractal-contracts/-/fractal-contracts-1.4.1.tgz", - "integrity": "sha512-QVbj/pqjxUesAKQxrBJCyr7+Y0g6zRgX7b69dwfFS8LVwIu1sstYG9hKTzKay0PGAy0LzA5+npBltTf5ikwDCg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@fractal-framework/fractal-contracts/-/fractal-contracts-1.4.2.tgz", + "integrity": "sha512-aUXxg+4lpyFh4L15QJnQS7x17if2F+hC1VtIVV+7F12XB6dOIi0FkZikR1nQglLcjbI2O/rlZfLD32o6C9DjDg==", "license": "MIT", "dependencies": { "@gnosis.pm/zodiac": "^1.1.4", diff --git a/package.json b/package.json index 357ab0cae1..7019342f62 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@fontsource/space-mono": "^5.0.19", - "@fractal-framework/fractal-contracts": "^1.4.1", + "@fractal-framework/fractal-contracts": "^1.4.2", "@graphprotocol/client-apollo": "^1.0.16", "@hatsprotocol/modules-sdk": "^1.4.0", "@hatsprotocol/sdk-v1-core": "^0.9.0", @@ -122,4 +122,4 @@ "url": "^0.11.0", "vitest": "^1.2.2" } -} +} \ No newline at end of file From fa66a844876626a1f92e8c14890bce7810da6287 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:41:59 -0500 Subject: [PATCH 06/14] EOL pretty fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7019342f62..0cab614b31 100644 --- a/package.json +++ b/package.json @@ -122,4 +122,4 @@ "url": "^0.11.0", "vitest": "^1.2.2" } -} \ No newline at end of file +} From c1b1e752db11f72ce51794ec97b69998a0a5377d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:46:23 -0500 Subject: [PATCH 07/14] Rename variable foundStreamIDs to assignedStreamed for clarity in useHatsTree hook --- src/hooks/DAO/loaders/useHatsTree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index a404fbfa3e..4b0f775baf 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -273,7 +273,7 @@ const useHatsTree = () => { } const payments: SablierPayment[] = []; if (hat.isTermed) { - const foundStreamIDs = hatIdsToStreamIds + const assignedStreamed = hatIdsToStreamIds .filter(item => item.hatId === BigInt(hat.id)) .map(({ streamId }) => { return streamId; @@ -283,7 +283,7 @@ const useHatsTree = () => { for (const recipient of uniqueRecipients) { payments.push( ...(await getPaymentStreams(recipient)).filter(stream => - foundStreamIDs.includes(stream.streamId), + assignedStreamed.includes(stream.streamId), ), ); } From 3e86f59dac7b62fd97d139dfd3322ba9d368a145 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:59:36 -0500 Subject: [PATCH 08/14] Add getTermedPaymentStreams function for improved payment stream filtering --- src/hooks/DAO/loaders/useHatsTree.ts | 48 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index 4b0f775baf..8bffc57bad 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -3,7 +3,7 @@ import { HatsSubgraphClient, Tree } from '@hatsprotocol/sdk-v1-subgraph'; import { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; -import { Address, formatUnits, getAddress, getContract } from 'viem'; +import { Address, formatUnits, getAddress, getContract, Hex } from 'viem'; import { usePublicClient } from 'wagmi'; import { StreamsQueryDocument } from '../../../../.graphclient'; import { SablierV2LockupLinearAbi } from '../../../assets/abi/SablierV2LockupLinear'; @@ -263,6 +263,23 @@ const useHatsTree = () => { [apolloClient, publicClient, sablierSubgraph], ); + const getTermedPaymentStreams = useCallback( + async (allTermRecipients: Address[], hatId: Hex): Promise => { + const assignedStreamIds = hatIdsToStreamIds + .filter(item => item.hatId === BigInt(hatId)) + .map(({ streamId }) => { + return streamId; + }); + const uniqueRecipients = [...new Set(allTermRecipients)]; + const payments: SablierPayment[] = []; + for (const recipient of uniqueRecipients) { + payments.push(...(await getPaymentStreams(recipient))); + } + return payments.filter(stream => assignedStreamIds.includes(stream.streamId)); + }, + [getPaymentStreams, hatIdsToStreamIds], + ); + useEffect(() => { async function getHatsStreams() { if (hatsTree && hatsTree.roleHats.length > 0 && !streamsFetched) { @@ -273,20 +290,12 @@ const useHatsTree = () => { } const payments: SablierPayment[] = []; if (hat.isTermed) { - const assignedStreamed = hatIdsToStreamIds - .filter(item => item.hatId === BigInt(hat.id)) - .map(({ streamId }) => { - return streamId; - }); - const recipients = hat.roleTerms.allTerms.map(term => term.nominee); - const uniqueRecipients = [...new Set(recipients)]; - for (const recipient of uniqueRecipients) { - payments.push( - ...(await getPaymentStreams(recipient)).filter(stream => - assignedStreamed.includes(stream.streamId), - ), - ); - } + payments.push( + ...(await getTermedPaymentStreams( + hat.roleTerms.allTerms.map(term => term.nominee), + hat.id, + )), + ); } else { if (!hat.smartAddress) { throw new Error('Smart account address not found'); @@ -303,7 +312,14 @@ const useHatsTree = () => { } getHatsStreams(); - }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched, hatIdsToStreamIds]); + }, [ + hatsTree, + updateRolesWithStreams, + getPaymentStreams, + streamsFetched, + hatIdsToStreamIds, + getTermedPaymentStreams, + ]); useEffect(() => { if (safeAddress && safe?.address && safeAddress !== safe.address && hatsTree) { From 8f787b67451a33d601a68ac6a087c4181d098188 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:19:43 -0500 Subject: [PATCH 09/14] let it just be the start of day selected --- src/components/ui/forms/DatePicker.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/ui/forms/DatePicker.tsx b/src/components/ui/forms/DatePicker.tsx index f9155727a6..57eaaf525e 100644 --- a/src/components/ui/forms/DatePicker.tsx +++ b/src/components/ui/forms/DatePicker.tsx @@ -195,15 +195,7 @@ export function DatePicker({ const handleDateChange = (e: OnDateChangeValue) => { if (e instanceof Date) { - // get the current time and then set selected date to it. - const currentHours = new Date().getHours(); - const currentMinutes = new Date().getMinutes(); - const currentSeconds = new Date().getSeconds(); - const currentMilliseconds = new Date().getMilliseconds(); - - onChange?.( - new Date(e.setHours(currentHours, currentMinutes, currentSeconds, currentMilliseconds)), - ); + onChange?.(e); onClose(); // Close the menu after date selection } }; From 47cf0bedf3ed92db7459be77c71dffa85e9d7f98 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:28:38 -0500 Subject: [PATCH 10/14] do not save to store --- src/hooks/DAO/loaders/useHatsTree.ts | 41 ++++++---------------------- src/hooks/DAO/useKeyValuePairs.ts | 1 + src/store/roles/rolesStoreUtils.ts | 1 - src/store/roles/useRolesStore.ts | 26 ++++++++++++++++-- src/types/roles.tsx | 1 - 5 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts index 8bffc57bad..0dbe4d0e27 100644 --- a/src/hooks/DAO/loaders/useHatsTree.ts +++ b/src/hooks/DAO/loaders/useHatsTree.ts @@ -3,7 +3,7 @@ import { HatsSubgraphClient, Tree } from '@hatsprotocol/sdk-v1-subgraph'; import { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; -import { Address, formatUnits, getAddress, getContract, Hex } from 'viem'; +import { Address, formatUnits, getAddress, getContract } from 'viem'; import { usePublicClient } from 'wagmi'; import { StreamsQueryDocument } from '../../../../.graphclient'; import { SablierV2LockupLinearAbi } from '../../../assets/abi/SablierV2LockupLinear'; @@ -39,7 +39,6 @@ const useHatsTree = () => { setHatsTree, updateRolesWithStreams, resetHatsStore, - hatIdsToStreamIds, } = useRolesStore(); const ipfsClient = useIPFSClient(); @@ -263,23 +262,6 @@ const useHatsTree = () => { [apolloClient, publicClient, sablierSubgraph], ); - const getTermedPaymentStreams = useCallback( - async (allTermRecipients: Address[], hatId: Hex): Promise => { - const assignedStreamIds = hatIdsToStreamIds - .filter(item => item.hatId === BigInt(hatId)) - .map(({ streamId }) => { - return streamId; - }); - const uniqueRecipients = [...new Set(allTermRecipients)]; - const payments: SablierPayment[] = []; - for (const recipient of uniqueRecipients) { - payments.push(...(await getPaymentStreams(recipient))); - } - return payments.filter(stream => assignedStreamIds.includes(stream.streamId)); - }, - [getPaymentStreams, hatIdsToStreamIds], - ); - useEffect(() => { async function getHatsStreams() { if (hatsTree && hatsTree.roleHats.length > 0 && !streamsFetched) { @@ -290,12 +272,12 @@ const useHatsTree = () => { } const payments: SablierPayment[] = []; if (hat.isTermed) { - payments.push( - ...(await getTermedPaymentStreams( - hat.roleTerms.allTerms.map(term => term.nominee), - hat.id, - )), - ); + const uniqueRecipients = [ + ...new Set(hat.roleTerms.allTerms.map(term => term.nominee)), + ]; + for (const recipient of uniqueRecipients) { + payments.push(...(await getPaymentStreams(recipient))); + } } else { if (!hat.smartAddress) { throw new Error('Smart account address not found'); @@ -312,14 +294,7 @@ const useHatsTree = () => { } getHatsStreams(); - }, [ - hatsTree, - updateRolesWithStreams, - getPaymentStreams, - streamsFetched, - hatIdsToStreamIds, - getTermedPaymentStreams, - ]); + }, [hatsTree, updateRolesWithStreams, getPaymentStreams, streamsFetched]); useEffect(() => { if (safeAddress && safe?.address && safeAddress !== safe.address && hatsTree) { diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index dcec653fd9..27507798a7 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -143,6 +143,7 @@ const useKeyValuePairs = () => { hatsTreeId: getHatsTreeId(logs, chain.id), contextChainId: chain.id, }); + setHatIdsToStreamIds(getHatIdsToStreamIds(logs, sablierV2LockupLinear, chain.id)); }, 20_000); }, }, diff --git a/src/store/roles/rolesStoreUtils.ts b/src/store/roles/rolesStoreUtils.ts index 33eb249e16..5db97f48a0 100644 --- a/src/store/roles/rolesStoreUtils.ts +++ b/src/store/roles/rolesStoreUtils.ts @@ -96,7 +96,6 @@ export const initialHatsStore: RolesStoreData = { decentHatsAddress: undefined, streamsFetched: false, contextChainId: null, - hatIdsToStreamIds: [], }; export const predictHatId = ({ adminHatId, hatsCount }: { adminHatId: Hex; hatsCount: number }) => { diff --git a/src/store/roles/useRolesStore.ts b/src/store/roles/useRolesStore.ts index e986ddcfc5..d3b65396b1 100644 --- a/src/store/roles/useRolesStore.ts +++ b/src/store/roles/useRolesStore.ts @@ -5,6 +5,15 @@ import { convertStreamIdToBigInt } from '../../hooks/streams/useCreateSablierStr import { DecentRoleHat, RolesStore } from '../../types/roles'; import { initialHatsStore, sanitize } from './rolesStoreUtils'; +const hatToStreamIdMap = new Map(); +const getHatToStreamIdMap = () => { + return Array.from(hatToStreamIdMap.entries()).map(([hatId, streamId]) => ({ + hatId, + streamId, + })); +}; +const setHatToStreamId = (key: BigInt, value: string) => hatToStreamIdMap.set(key, value); + const useRolesStore = create()((set, get) => ({ ...initialHatsStore, getHat: hatId => { @@ -96,10 +105,21 @@ const useRolesStore = create()((set, get) => ({ updateRolesWithStreams: (updatedRoles: DecentRoleHat[]) => { const existingHatsTree = get().hatsTree; if (!existingHatsTree) return; + const hatIdToStreamIdMap = getHatToStreamIdMap(); const updatedDecentTree = { ...existingHatsTree, - roleHats: updatedRoles, + roleHats: updatedRoles.map(roleHat => { + const filteredStreamIds = hatIdToStreamIdMap + .filter(hatToStreamId => hatToStreamId.hatId === BigInt(roleHat.id)) + .map(hatToStreamId => hatToStreamId.streamId); + return { + ...roleHat, + payments: roleHat.payments?.filter(payment => { + return filteredStreamIds.includes(payment.streamId); + }), + }; + }), }; set(() => ({ hatsTree: updatedDecentTree, streamsFetched: true })); @@ -130,7 +150,9 @@ const useRolesStore = create()((set, get) => ({ })); }, setHatIdsToStreamIds: hatIdsToStreamIds => { - set(() => ({ hatIdsToStreamIds })); + for (const { hatId, streamId } of hatIdsToStreamIds) { + setHatToStreamId(hatId, streamId); + } }, resetHatsStore: () => set(() => initialHatsStore), })); diff --git a/src/types/roles.tsx b/src/types/roles.tsx index 5e7786987f..f82da27e3a 100644 --- a/src/types/roles.tsx +++ b/src/types/roles.tsx @@ -238,7 +238,6 @@ export interface RolesStoreData { hatsTree: undefined | null | DecentTree; streamsFetched: boolean; contextChainId: number | null; - hatIdsToStreamIds: { hatId: BigInt; streamId: string }[]; } export interface RolesStore extends RolesStoreData { From a7e410aa12365b7ebc4d24ff600e10f742db65b7 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Sat, 23 Nov 2024 13:12:09 -0500 Subject: [PATCH 11/14] remove break out --- src/store/roles/useRolesStore.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/store/roles/useRolesStore.ts b/src/store/roles/useRolesStore.ts index d3b65396b1..e746984e21 100644 --- a/src/store/roles/useRolesStore.ts +++ b/src/store/roles/useRolesStore.ts @@ -12,7 +12,6 @@ const getHatToStreamIdMap = () => { streamId, })); }; -const setHatToStreamId = (key: BigInt, value: string) => hatToStreamIdMap.set(key, value); const useRolesStore = create()((set, get) => ({ ...initialHatsStore, @@ -151,7 +150,7 @@ const useRolesStore = create()((set, get) => ({ }, setHatIdsToStreamIds: hatIdsToStreamIds => { for (const { hatId, streamId } of hatIdsToStreamIds) { - setHatToStreamId(hatId, streamId); + hatToStreamIdMap.set(hatId, streamId); } }, resetHatsStore: () => set(() => initialHatsStore), From d038852f3e4ef51e03486494e3e554810d0a38a5 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Sun, 24 Nov 2024 00:18:55 -0500 Subject: [PATCH 12/14] switch up logic to allow multiple streams per hat to be stored --- src/hooks/DAO/useKeyValuePairs.ts | 8 ++++---- src/store/roles/useRolesStore.ts | 22 +++++++++++----------- src/types/roles.tsx | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index 27507798a7..21909081bb 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -98,7 +98,7 @@ const useKeyValuePairs = () => { chain, contracts: { keyValuePairs, sablierV2LockupLinear }, } = useNetworkConfig(); - const { setHatsTreeId, setHatIdsToStreamIds } = useRolesStore(); + const { setHatsTreeId, setStreamIdsToHatIds } = useRolesStore(); const [searchParams] = useSearchParams(); const safeAddress = node.safe?.address; @@ -122,7 +122,7 @@ const useKeyValuePairs = () => { contextChainId: chain.id, hatsTreeId: getHatsTreeId(safeEvents, chain.id), }); - setHatIdsToStreamIds(getHatIdsToStreamIds(safeEvents, sablierV2LockupLinear, chain.id)); + setStreamIdsToHatIds(getHatIdsToStreamIds(safeEvents, sablierV2LockupLinear, chain.id)); }) .catch(error => { setHatsTreeId({ hatsTreeId: null, contextChainId: chain.id }); @@ -143,7 +143,7 @@ const useKeyValuePairs = () => { hatsTreeId: getHatsTreeId(logs, chain.id), contextChainId: chain.id, }); - setHatIdsToStreamIds(getHatIdsToStreamIds(logs, sablierV2LockupLinear, chain.id)); + setStreamIdsToHatIds(getHatIdsToStreamIds(logs, sablierV2LockupLinear, chain.id)); }, 20_000); }, }, @@ -158,7 +158,7 @@ const useKeyValuePairs = () => { publicClient, searchParams, setHatsTreeId, - setHatIdsToStreamIds, + setStreamIdsToHatIds, sablierV2LockupLinear, ]); }; diff --git a/src/store/roles/useRolesStore.ts b/src/store/roles/useRolesStore.ts index e746984e21..68752e1b82 100644 --- a/src/store/roles/useRolesStore.ts +++ b/src/store/roles/useRolesStore.ts @@ -5,11 +5,11 @@ import { convertStreamIdToBigInt } from '../../hooks/streams/useCreateSablierStr import { DecentRoleHat, RolesStore } from '../../types/roles'; import { initialHatsStore, sanitize } from './rolesStoreUtils'; -const hatToStreamIdMap = new Map(); -const getHatToStreamIdMap = () => { - return Array.from(hatToStreamIdMap.entries()).map(([hatId, streamId]) => ({ - hatId, +const streamIdToHatIdMap = new Map(); +const getStreamIdToHatIdMap = () => { + return Array.from(streamIdToHatIdMap.entries()).map(([streamId, hatId]) => ({ streamId, + hatId, })); }; @@ -104,14 +104,14 @@ const useRolesStore = create()((set, get) => ({ updateRolesWithStreams: (updatedRoles: DecentRoleHat[]) => { const existingHatsTree = get().hatsTree; if (!existingHatsTree) return; - const hatIdToStreamIdMap = getHatToStreamIdMap(); + const streamIdsToHatIdsMap = getStreamIdToHatIdMap(); const updatedDecentTree = { ...existingHatsTree, roleHats: updatedRoles.map(roleHat => { - const filteredStreamIds = hatIdToStreamIdMap - .filter(hatToStreamId => hatToStreamId.hatId === BigInt(roleHat.id)) - .map(hatToStreamId => hatToStreamId.streamId); + const filteredStreamIds = streamIdsToHatIdsMap + .filter(ids => ids.hatId === BigInt(roleHat.id)) + .map(ids => ids.streamId); return { ...roleHat, payments: roleHat.payments?.filter(payment => { @@ -148,9 +148,9 @@ const useRolesStore = create()((set, get) => ({ }, })); }, - setHatIdsToStreamIds: hatIdsToStreamIds => { - for (const { hatId, streamId } of hatIdsToStreamIds) { - hatToStreamIdMap.set(hatId, streamId); + setStreamIdsToHatIds: streamIdsToHatIds => { + for (const { hatId, streamId } of streamIdsToHatIds) { + streamIdToHatIdMap.set(streamId, hatId); } }, resetHatsStore: () => set(() => initialHatsStore), diff --git a/src/types/roles.tsx b/src/types/roles.tsx index f82da27e3a..0437c075b2 100644 --- a/src/types/roles.tsx +++ b/src/types/roles.tsx @@ -244,7 +244,7 @@ export interface RolesStore extends RolesStoreData { getHat: (hatId: Hex) => DecentRoleHat | null; getPayment: (hatId: Hex, streamId: string) => SablierPayment | null; setHatsTreeId: (args: { contextChainId: number | null; hatsTreeId?: number | null }) => void; - setHatIdsToStreamIds: (hatIdsToStreamIds: { hatId: BigInt; streamId: string }[]) => void; + setStreamIdsToHatIds: (streamIdsToHatIds: { hatId: BigInt; streamId: string }[]) => void; setHatsTree: (params: { hatsTree: Tree | null | undefined; chainId: bigint; From 796b30276bc90a40276ea4a1c51c833faef0187d Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:18:45 -0500 Subject: [PATCH 13/14] Refactor roles store to consolidate hat key-value pair data handling --- src/hooks/DAO/useKeyValuePairs.ts | 21 ++++++++++++--------- src/store/roles/useRolesStore.ts | 17 +++++++++-------- src/types/roles.tsx | 7 +++++-- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/hooks/DAO/useKeyValuePairs.ts b/src/hooks/DAO/useKeyValuePairs.ts index 21909081bb..38615ef52c 100644 --- a/src/hooks/DAO/useKeyValuePairs.ts +++ b/src/hooks/DAO/useKeyValuePairs.ts @@ -98,7 +98,7 @@ const useKeyValuePairs = () => { chain, contracts: { keyValuePairs, sablierV2LockupLinear }, } = useNetworkConfig(); - const { setHatsTreeId, setStreamIdsToHatIds } = useRolesStore(); + const { setHatKeyValuePairData } = useRolesStore(); const [searchParams] = useSearchParams(); const safeAddress = node.safe?.address; @@ -118,14 +118,18 @@ const useKeyValuePairs = () => { keyValuePairsContract.getEvents .ValueUpdated({ theAddress: safeAddress }, { fromBlock: 0n }) .then(safeEvents => { - setHatsTreeId({ + setHatKeyValuePairData({ contextChainId: chain.id, hatsTreeId: getHatsTreeId(safeEvents, chain.id), + streamIdsToHatIds: getHatIdsToStreamIds(safeEvents, sablierV2LockupLinear, chain.id), }); - setStreamIdsToHatIds(getHatIdsToStreamIds(safeEvents, sablierV2LockupLinear, chain.id)); }) .catch(error => { - setHatsTreeId({ hatsTreeId: null, contextChainId: chain.id }); + setHatKeyValuePairData({ + hatsTreeId: null, + contextChainId: chain.id, + streamIdsToHatIds: [], + }); logError(error); }); @@ -139,11 +143,11 @@ const useKeyValuePairs = () => { // time to index, and do that most cleanly by not even telling the rest // of our code that we have the hats tree id until some time has passed. setTimeout(() => { - setHatsTreeId({ - hatsTreeId: getHatsTreeId(logs, chain.id), + setHatKeyValuePairData({ contextChainId: chain.id, + hatsTreeId: getHatsTreeId(logs, chain.id), + streamIdsToHatIds: getHatIdsToStreamIds(logs, sablierV2LockupLinear, chain.id), }); - setStreamIdsToHatIds(getHatIdsToStreamIds(logs, sablierV2LockupLinear, chain.id)); }, 20_000); }, }, @@ -157,8 +161,7 @@ const useKeyValuePairs = () => { safeAddress, publicClient, searchParams, - setHatsTreeId, - setStreamIdsToHatIds, + setHatKeyValuePairData, sablierV2LockupLinear, ]); }; diff --git a/src/store/roles/useRolesStore.ts b/src/store/roles/useRolesStore.ts index 68752e1b82..77c59b1373 100644 --- a/src/store/roles/useRolesStore.ts +++ b/src/store/roles/useRolesStore.ts @@ -47,16 +47,21 @@ const useRolesStore = create()((set, get) => ({ return matches[0]; }, - setHatsTreeId: args => + setHatKeyValuePairData: args => { + const { hatsTreeId, contextChainId, streamIdsToHatIds } = args; + for (const { hatId, streamId } of streamIdsToHatIds) { + streamIdToHatIdMap.set(streamId, hatId); + } set(() => { - const { hatsTreeId, contextChainId } = args; // if `hatsTreeId` is null or undefined, // set `hatsTree` to that same value if (typeof hatsTreeId !== 'number') { return { hatsTreeId, hatsTree: hatsTreeId, streamsFetched: false, contextChainId: null }; } return { hatsTreeId, streamsFetched: false, contextChainId }; - }), + }); + }, + setHatsTree: async params => { const hatsTree = await sanitize( params.hatsTree, @@ -148,11 +153,7 @@ const useRolesStore = create()((set, get) => ({ }, })); }, - setStreamIdsToHatIds: streamIdsToHatIds => { - for (const { hatId, streamId } of streamIdsToHatIds) { - streamIdToHatIdMap.set(streamId, hatId); - } - }, + resetHatsStore: () => set(() => initialHatsStore), })); diff --git a/src/types/roles.tsx b/src/types/roles.tsx index 0437c075b2..71c9fe1339 100644 --- a/src/types/roles.tsx +++ b/src/types/roles.tsx @@ -243,8 +243,11 @@ export interface RolesStoreData { export interface RolesStore extends RolesStoreData { getHat: (hatId: Hex) => DecentRoleHat | null; getPayment: (hatId: Hex, streamId: string) => SablierPayment | null; - setHatsTreeId: (args: { contextChainId: number | null; hatsTreeId?: number | null }) => void; - setStreamIdsToHatIds: (streamIdsToHatIds: { hatId: BigInt; streamId: string }[]) => void; + setHatKeyValuePairData: (args: { + contextChainId: number | null; + hatsTreeId?: number | null; + streamIdsToHatIds: { hatId: BigInt; streamId: string }[]; + }) => void; setHatsTree: (params: { hatsTree: Tree | null | undefined; chainId: bigint; From cf7b133a62d222902f2b432aac53bd24a494bc87 Mon Sep 17 00:00:00 2001 From: David Colon <38386583+Da-Colon@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:25:29 -0500 Subject: [PATCH 14/14] only filter for isTermed (for now) --- src/store/roles/useRolesStore.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/store/roles/useRolesStore.ts b/src/store/roles/useRolesStore.ts index 77c59b1373..3f6da74f19 100644 --- a/src/store/roles/useRolesStore.ts +++ b/src/store/roles/useRolesStore.ts @@ -119,9 +119,11 @@ const useRolesStore = create()((set, get) => ({ .map(ids => ids.streamId); return { ...roleHat, - payments: roleHat.payments?.filter(payment => { - return filteredStreamIds.includes(payment.streamId); - }), + payments: roleHat.isTermed + ? roleHat.payments?.filter(payment => { + return filteredStreamIds.includes(payment.streamId); + }) + : roleHat.payments, }; }), };