Skip to content

Commit

Permalink
Merge pull request #2562 from decentdao/issue/2479-tie-streams-to-hat
Browse files Browse the repository at this point in the history
`[Issue #2479]` Tie streams to hat
  • Loading branch information
Da-Colon authored Nov 28, 2024
2 parents a17cae1 + cf7b133 commit 4629cb0
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 25 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@chakra-ui/anatomy": "^2.2.2",
"@chakra-ui/react": "^2.8.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",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/forms/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export function DatePicker({

const handleDateChange = (e: OnDateChangeValue) => {
if (e instanceof Date) {
onChange?.(new Date(e.setHours(0, 0, 0, 0)));
onChange?.(e);
onClose(); // Close the menu after date selection
}
};
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/DAO/loaders/useHatsTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,10 @@ const useHatsTree = () => {
return hat;
}
const payments: SablierPayment[] = [];
// @todo - update Datepicker to choose more precise dates (Date.now())
if (hat.isTermed) {
const recipients = hat.roleTerms.allTerms.map(term => term.nominee);
const uniqueRecipients = [...new Set(recipients)];
const uniqueRecipients = [
...new Set(hat.roleTerms.allTerms.map(term => term.nominee)),
];
for (const recipient of uniqueRecipients) {
payments.push(...(await getPaymentStreams(recipient)));
}
Expand Down
72 changes: 61 additions & 11 deletions src/hooks/DAO/useKeyValuePairs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -55,14 +55,50 @@ const getHatsTreeId = (
}
};

const getHatIdsToStreamIds = (
events: GetContractEventsReturnType<typeof abis.KeyValuePairs> | 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 { setHatKeyValuePairData } = useRolesStore();
const [searchParams] = useSearchParams();

const safeAddress = node.safe?.address;
Expand All @@ -81,14 +117,19 @@ const useKeyValuePairs = () => {
});
keyValuePairsContract.getEvents
.ValueUpdated({ theAddress: safeAddress }, { fromBlock: 0n })
.then(safeEvents =>
setHatsTreeId({
.then(safeEvents => {
setHatKeyValuePairData({
contextChainId: chain.id,
hatsTreeId: getHatsTreeId(safeEvents, chain.id),
}),
)
streamIdsToHatIds: getHatIdsToStreamIds(safeEvents, sablierV2LockupLinear, chain.id),
});
})
.catch(error => {
setHatsTreeId({ hatsTreeId: null, contextChainId: chain.id });
setHatKeyValuePairData({
hatsTreeId: null,
contextChainId: chain.id,
streamIdsToHatIds: [],
});
logError(error);
});

Expand All @@ -102,9 +143,10 @@ 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),
});
}, 20_000);
},
Expand All @@ -113,7 +155,15 @@ const useKeyValuePairs = () => {
return () => {
unwatch();
};
}, [chain.id, keyValuePairs, safeAddress, publicClient, searchParams, setHatsTreeId]);
}, [
chain.id,
keyValuePairs,
safeAddress,
publicClient,
searchParams,
setHatKeyValuePairData,
sablierV2LockupLinear,
]);
};

export { useKeyValuePairs };
2 changes: 2 additions & 0 deletions src/hooks/utils/useCreateRoles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ export default function useCreateRoles() {
hats: [hatStruct],
topHatId: BigInt(hatsTree.topHat.id),
topHatAccount: hatsTree.topHat.smartAddress,
keyValuePairs,
},
],
});
Expand Down Expand Up @@ -670,6 +671,7 @@ export default function useCreateRoles() {
erc6551Registry,
hatsAccount1ofNMasterCopy,
hatsElectionsEligibilityMasterCopy,
keyValuePairs,
],
);

Expand Down
35 changes: 31 additions & 4 deletions src/store/roles/useRolesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import { convertStreamIdToBigInt } from '../../hooks/streams/useCreateSablierStr
import { DecentRoleHat, RolesStore } from '../../types/roles';
import { initialHatsStore, sanitize } from './rolesStoreUtils';

const streamIdToHatIdMap = new Map<string, BigInt>();
const getStreamIdToHatIdMap = () => {
return Array.from(streamIdToHatIdMap.entries()).map(([streamId, hatId]) => ({
streamId,
hatId,
}));
};

const useRolesStore = create<RolesStore>()((set, get) => ({
...initialHatsStore,
getHat: hatId => {
Expand Down Expand Up @@ -39,16 +47,21 @@ const useRolesStore = create<RolesStore>()((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,
Expand Down Expand Up @@ -96,10 +109,23 @@ const useRolesStore = create<RolesStore>()((set, get) => ({
updateRolesWithStreams: (updatedRoles: DecentRoleHat[]) => {
const existingHatsTree = get().hatsTree;
if (!existingHatsTree) return;
const streamIdsToHatIdsMap = getStreamIdToHatIdMap();

const updatedDecentTree = {
...existingHatsTree,
roleHats: updatedRoles,
roleHats: updatedRoles.map(roleHat => {
const filteredStreamIds = streamIdsToHatIdsMap
.filter(ids => ids.hatId === BigInt(roleHat.id))
.map(ids => ids.streamId);
return {
...roleHat,
payments: roleHat.isTermed
? roleHat.payments?.filter(payment => {
return filteredStreamIds.includes(payment.streamId);
})
: roleHat.payments,
};
}),
};

set(() => ({ hatsTree: updatedDecentTree, streamsFetched: true }));
Expand Down Expand Up @@ -129,6 +155,7 @@ const useRolesStore = create<RolesStore>()((set, get) => ({
},
}));
},

resetHatsStore: () => set(() => initialHatsStore),
}));

Expand Down
6 changes: 5 additions & 1 deletion src/types/roles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +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;
setHatKeyValuePairData: (args: {
contextChainId: number | null;
hatsTreeId?: number | null;
streamIdsToHatIds: { hatId: BigInt; streamId: string }[];
}) => void;
setHatsTree: (params: {
hatsTree: Tree | null | undefined;
chainId: bigint;
Expand Down

0 comments on commit 4629cb0

Please sign in to comment.