From c737b3a6a34cafd4a5ff4ec3a1ec87711fd04052 Mon Sep 17 00:00:00 2001 From: MrX-SNX Date: Sat, 31 Aug 2024 20:24:05 +0100 Subject: [PATCH] feat: added multisig profile contract (#428) * feat: added multisig profile contract * feat: querying from contract if address is multisig * ref: update address * feat: added address to object --- .../mutations/useUpdateUserDetailsMutation.ts | 90 +++++++++++-------- .../ui/src/queries/useGetUserDetailsQuery.ts | 52 ++++++++--- governance/ui/src/utils/abi.ts | 38 ++++++++ governance/ui/src/utils/contracts.ts | 7 +- 4 files changed, 138 insertions(+), 49 deletions(-) diff --git a/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts b/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts index 57cb232b4..d0b72d686 100644 --- a/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts +++ b/governance/ui/src/mutations/useUpdateUserDetailsMutation.ts @@ -12,6 +12,7 @@ import { useGetIsUUIDValidQuery } from '../queries/'; import { utils } from 'ethers'; import { GetUserDetails } from '../queries/useGetUserDetailsQuery'; import { useWallet, useSigner } from '../queries/useWallet'; +import { profileContract } from '../utils/contracts'; type UpdateUserDetailsResponse = { data: GetUserDetails & { @@ -107,47 +108,66 @@ function useUpdateUserDetailsMutation() { return useMutation({ mutationKey: ['updateUserDetails'], mutationFn: async (userProfile: GetUserDetails) => { - let signedInUuid = ''; - if (!isUuidValidQuery.data) { - signedInUuid = (await boardroomSignIn()) || ''; - } - if (activeWallet?.address) { - const body = { - ...userProfile, - uuid: signedInUuid, - }; - const updateUserDetailsResponse = await fetch( - UPDATE_USER_DETAILS_API_URL(activeWallet.address), + const address = await signer?.getAddress(); + const isContract = await signer?.provider.getCode(address || ''); + if (isContract !== '0x' && signer) { + await profileContract.connect(signer).updateProfile( + { + username: userProfile.username, + about: userProfile.about, + github: userProfile.github, + twitter: userProfile.twitter, + discord: userProfile.discord, + delegationPitch: userProfile.delegationPitch, + }, { - method: 'POST', - body: JSON.stringify(body), + maxPriorityFeePerGas: utils.parseUnits('1', 'gwei'), + maxFeePerGas: utils.parseUnits('2', 'gwei'), } ); - - const updateUserDetailsResult = - (await updateUserDetailsResponse.json()) as UpdateUserDetailsResponse; - - let updateDelegationPitchResult = { - data: {}, - }; - - if (userProfile.delegationPitch) { - const delegationPitchesBody = { - protocol: 'synthetix', - address: activeWallet.address, - delegationPitch: userProfile.delegationPitch, + } else { + let signedInUuid = ''; + if (!isUuidValidQuery.data) { + signedInUuid = (await boardroomSignIn()) || ''; + } + if (activeWallet?.address) { + const body = { + ...userProfile, uuid: signedInUuid, }; - const delegationUpdateReponse = await fetch(UPDATE_USER_PITCH_FOR_PROTOCOL, { - method: 'POST', - body: JSON.stringify(delegationPitchesBody), - }); - updateDelegationPitchResult = await delegationUpdateReponse.json(); - } + const updateUserDetailsResponse = await fetch( + UPDATE_USER_DETAILS_API_URL(activeWallet.address), + { + method: 'POST', + body: JSON.stringify(body), + } + ); + + const updateUserDetailsResult = + (await updateUserDetailsResponse.json()) as UpdateUserDetailsResponse; + + let updateDelegationPitchResult = { + data: {}, + }; - return { ...updateUserDetailsResult.data, ...updateDelegationPitchResult.data }; - } else { - return new Error(); + if (userProfile.delegationPitch) { + const delegationPitchesBody = { + protocol: 'synthetix', + address: activeWallet.address, + delegationPitch: userProfile.delegationPitch, + uuid: signedInUuid, + }; + const delegationUpdateReponse = await fetch(UPDATE_USER_PITCH_FOR_PROTOCOL, { + method: 'POST', + body: JSON.stringify(delegationPitchesBody), + }); + updateDelegationPitchResult = await delegationUpdateReponse.json(); + } + + return { ...updateUserDetailsResult.data, ...updateDelegationPitchResult.data }; + } else { + return new Error(); + } } }, diff --git a/governance/ui/src/queries/useGetUserDetailsQuery.ts b/governance/ui/src/queries/useGetUserDetailsQuery.ts index 494679999..c8e835a00 100644 --- a/governance/ui/src/queries/useGetUserDetailsQuery.ts +++ b/governance/ui/src/queries/useGetUserDetailsQuery.ts @@ -1,5 +1,7 @@ import { GET_PITCHES_FOR_USER_API_URL, GET_USER_DETAILS_API_URL } from '../utils/boardroom'; import { useQuery } from '@tanstack/react-query'; +import { motherShipProvider } from '../utils/providers'; +import { profileContract } from '../utils/contracts'; export type GetUserDetails = { address: string; @@ -46,9 +48,20 @@ export async function getUserDetails( ): Promise<(T extends string ? GetUserDetails : GetUserDetails[]) | undefined> { if (typeof walletAddress === 'string') { const randomNumber = Math.random(); + const isMultiSig = await motherShipProvider(2192).getCode(walletAddress); + if (isMultiSig !== '0x') { + const profile = await profileContract + .connect(motherShipProvider(2192)) + .getProfile(walletAddress); + + return { ...profile, address: walletAddress } as T extends string + ? GetUserDetails + : GetUserDetails[]; + } const userDetailsResponse = await fetch(GET_USER_DETAILS_API_URL(walletAddress), { method: 'POST', }); + const userProfile = await userDetailsResponse.json(); const userPitchesResponse = await fetch( GET_PITCHES_FOR_USER_API_URL(walletAddress, randomNumber), @@ -83,6 +96,18 @@ export async function getUserDetails( }) ) ); + + const multiSigQueries = await Promise.all( + walletAddress.map(async (address) => await motherShipProvider(2192).getCode(address)) + ); + + const multiSigs = multiSigQueries.filter((sig) => sig !== '0x'); + const multiSigsProfiles = await Promise.all( + multiSigs.map(async (address) => ({ + ...(await profileContract.connect(motherShipProvider(2192)).getProfile(address)), + address, + })) + ); const userProfile = await Promise.all( userDetailsResponse.map(async (responses) => await responses.json()) ); @@ -103,18 +128,19 @@ export async function getUserDetails( return data.delegationPitches?.filter((e: UserPitch) => e.protocol === 'synthetix'); }); } - - return userProfile.map(({ data }) => { - try { - delete data.delegationPitches; - return { - ...data, - delegationPitch: foundPitch, - }; - } catch (error) { - console.error(error); - return userProfile; - } - }) as T extends string ? GetUserDetails : GetUserDetails[]; + return userProfile + .map(({ data }) => { + try { + delete data.delegationPitches; + return { + ...data, + delegationPitch: foundPitch, + }; + } catch (error) { + console.error(error); + return userProfile; + } + }) + .concat(multiSigsProfiles) as T extends string ? GetUserDetails : GetUserDetails[]; } } diff --git a/governance/ui/src/utils/abi.ts b/governance/ui/src/utils/abi.ts index a17f61e3d..729525104 100644 --- a/governance/ui/src/utils/abi.ts +++ b/governance/ui/src/utils/abi.ts @@ -1,3 +1,41 @@ +export const profileAbi = [ + { + inputs: [{ internalType: 'address', name: 'user', type: 'address' }], + name: 'getProfile', + outputs: [ + { internalType: 'string', name: 'username', type: 'string' }, + { internalType: 'string', name: 'about', type: 'string' }, + { internalType: 'string', name: 'twitter', type: 'string' }, + { internalType: 'string', name: 'github', type: 'string' }, + { internalType: 'string', name: 'discord', type: 'string' }, + { internalType: 'string', name: 'delegationPitch', type: 'string' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'string', name: 'username', type: 'string' }, + { internalType: 'string', name: 'about', type: 'string' }, + { internalType: 'string', name: 'twitter', type: 'string' }, + { internalType: 'string', name: 'github', type: 'string' }, + { internalType: 'string', name: 'discord', type: 'string' }, + { internalType: 'string', name: 'delegationPitch', type: 'string' }, + ], + internalType: 'struct UserProfile.ProfileUpdate', + name: 'update', + type: 'tuple', + }, + ], + name: 'updateProfile', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; + export const multicallABI = [ { inputs: [], diff --git a/governance/ui/src/utils/contracts.ts b/governance/ui/src/utils/contracts.ts index 8e6559f1f..23a3e9b4b 100644 --- a/governance/ui/src/utils/contracts.ts +++ b/governance/ui/src/utils/contracts.ts @@ -1,5 +1,5 @@ import { Contract } from 'ethers'; -import { electionModuleABITest } from './abi'; +import { electionModuleABITest, profileAbi } from './abi'; import { CouncilSlugs } from './councils'; export const isMotherchain = (chainId?: string | number) => { @@ -57,3 +57,8 @@ const abiForSnapshotMock = [ 'function balanceOfOnPeriod(address, uint256) view returns (uint256)', 'function setBalanceOfOnPeriod(address, uint256, uint256) external', ]; + +export const profileContract = new Contract( + '0x4F94C98825517688C088d565D3364B01748149a0', + profileAbi +);