diff --git a/src/app/delegate/api/register/route.test.ts b/src/app/delegate/api/register/route.test.ts index 966fdea..f9479f3 100644 --- a/src/app/delegate/api/register/route.test.ts +++ b/src/app/delegate/api/register/route.test.ts @@ -5,7 +5,7 @@ import fs from 'fs'; import path from 'path'; import * as utils from 'src/features/delegation/utils'; import { getValidRequest } from 'src/test/delegatee-registration-utils'; -import { vi } from 'vitest'; +import { expect, it, vi } from 'vitest'; import { POST } from './route'; const getRequest = (formData: FormData): Request => { @@ -67,7 +67,7 @@ it('successfuly calls PR creation', async () => { }, "interests": "blockchain, NFTs", "name": "Delegatee name", - "signature": "0xe37dfb7abd8d7668057189d980fed5ccc08716ab45cabd70638305b91366c8693fb307fc39b5d8c46cebafee318bffa4a4df9d80f7a40b121613cdcb6cc582051c", + "signature": "0x52a3c23ef6c6817691872b77615ef30927453d641acd8c607de458d39e581bcd5411f723102640897af151644086abf4f3a9baf216d684d784194aef2c6730be1c", "twitterUrl": "https://example.com/x", "verificationUrl": "https://example.com/verification", "websiteUrl": "https://example.com", diff --git a/src/features/delegation/DelegateRegistrationForm.tsx b/src/features/delegation/DelegateRegistrationForm.tsx index 51b8a6b..89105ea 100644 --- a/src/features/delegation/DelegateRegistrationForm.tsx +++ b/src/features/delegation/DelegateRegistrationForm.tsx @@ -12,6 +12,7 @@ import { RegisterDelegateResponseStatus, } from 'src/features/delegation/types'; import { validateRegistrationRequest } from 'src/features/delegation/validateRegistrationRequest'; +import { logger } from 'src/utils/logger'; import { useAccount } from 'wagmi'; const initialValues: RegisterDelegateFormValues = { @@ -81,13 +82,25 @@ export function DelegateRegistrationForm({ ...initialValues, }} onSubmit={async (values) => { + let signature: HexString; + setIsSubmitting(true); setIsSigning(true); - const signature = await signForm({ - ...values, - address: address! - }); + try { + signature = await signForm({ + ...values, + address: address!, + image: imageFile + }); + } catch (err) { + setIsSubmitting(false); + + toast.error('Error while signing message'); + logger.error(err); + + return; + } setIsSigning(false); diff --git a/src/features/delegation/hooks/useSIgnedData.ts b/src/features/delegation/hooks/useSIgnedData.ts index 6f12e5c..d53a00b 100644 --- a/src/features/delegation/hooks/useSIgnedData.ts +++ b/src/features/delegation/hooks/useSIgnedData.ts @@ -1,19 +1,22 @@ import { EIP712Delegatee, RegisterDelegateFormValues } from 'src/features/delegation/types'; +import { sha256 } from 'viem'; import { useSignTypedData } from 'wagmi'; export function useSignedData() { const { signTypedDataAsync } = useSignTypedData(); - return ({ - name, - address, - verificationUrl, - }: Pick) => { + + return async (values: RegisterDelegateFormValues) => { + if (!values.image) { + throw new Error('Image required'); + } + return signTypedDataAsync({ ...EIP712Delegatee, message: { - name, - address, - verificationUrl, + ...values, + imageSha: sha256(new Uint8Array(await values.image.arrayBuffer())), + websiteUrl: values.websiteUrl || '', + twitterUrl: values.twitterUrl || '', }, }); }; diff --git a/src/features/delegation/types.ts b/src/features/delegation/types.ts index 6b4e3cd..d7fd07c 100644 --- a/src/features/delegation/types.ts +++ b/src/features/delegation/types.ts @@ -80,9 +80,14 @@ export type Delegatee = DelegateeMetadata & { export const EIP712Delegatee = { types: { Delegatee: [ - { name: 'name', type: 'string' }, { name: 'address', type: 'address' }, + { name: 'name', type: 'string' }, { name: 'verificationUrl', type: 'string' }, + { name: 'websiteUrl', type: 'string' }, + { name: 'twitterUrl', type: 'string' }, + { name: 'interests', type: 'string' }, + { name: 'description', type: 'string' }, + { name: 'imageSha', type: 'string' }, ], }, primaryType: 'Delegatee', diff --git a/src/features/delegation/validateRegistrationRequest.test.ts b/src/features/delegation/validateRegistrationRequest.test.ts index a4c34d8..b66993f 100644 --- a/src/features/delegation/validateRegistrationRequest.test.ts +++ b/src/features/delegation/validateRegistrationRequest.test.ts @@ -2,7 +2,7 @@ import { RegisterDelegateRequest } from 'src/features/delegation/types'; import * as utils from 'src/features/delegation/utils'; import { validateRegistrationRequest } from 'src/features/delegation/validateRegistrationRequest'; import { getValidRequest } from 'src/test/delegatee-registration-utils'; -import { vi } from 'vitest'; +import { expect, it, vi } from 'vitest'; it('validates successfully', async () => { const request = await getValidRequest(); @@ -88,6 +88,7 @@ it('validates wrong URLs', async () => { expect(await validateRegistrationRequest(request, true)).toMatchInlineSnapshot(` { + "signature": "Invalid signature", "twitterUrl": "Invalid url", "websiteUrl": "Invalid url", } @@ -106,6 +107,7 @@ it('validates invalid image', async () => { expect(await validateRegistrationRequest(request, true)).toMatchInlineSnapshot(` { "image": "Invalid image", + "signature": "Invalid signature", } `); }); diff --git a/src/features/delegation/validateRegistrationRequest.ts b/src/features/delegation/validateRegistrationRequest.ts index 72c4723..095c596 100644 --- a/src/features/delegation/validateRegistrationRequest.ts +++ b/src/features/delegation/validateRegistrationRequest.ts @@ -4,7 +4,7 @@ import { RegisterDelegateRequest, } from 'src/features/delegation/types'; import { isAddressAnAccount } from 'src/features/delegation/utils'; -import { isAddress, verifyTypedData } from 'viem'; +import { isAddress, sha256, verifyTypedData } from 'viem'; export const validateRegistrationRequest = async ( request: RegisterDelegateRequest, @@ -92,20 +92,28 @@ function toSchemaConformantValues(request: RegisterDelegateRequest) { return values; } -function verifySigner(request: RegisterDelegateRequest) { +const verifySigner = async (request: RegisterDelegateRequest) => { if (!request.signature) { throw new Error('Signature required'); } + if (!request.image) { + throw new Error('Image required'); + } + return verifyTypedData({ ...EIP712Delegatee, address: request.address, signature: request.signature, message: { - // TODO add remaining fields address: request.address, name: request.name, verificationUrl: request.verificationUrl, + websiteUrl: request.websiteUrl || '', + twitterUrl: request.twitterUrl || '', + interests: request.interests, + description: request.description, + imageSha: sha256(new Uint8Array(await request.image.arrayBuffer())), }, }); -} +}; diff --git a/src/test/delegatee-registration-utils.ts b/src/test/delegatee-registration-utils.ts index 5e71093..9bc8428 100644 --- a/src/test/delegatee-registration-utils.ts +++ b/src/test/delegatee-registration-utils.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import { EIP712Delegatee, RegisterDelegateRequest } from 'src/features/delegation/types'; -import { createWalletClient, http } from 'viem'; +import { createWalletClient, http, sha256 } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { celo } from 'viem/chains'; @@ -27,12 +27,24 @@ export const getValidRequest = async (): Promise => { transport: http(), }); + const requestWithoutSignature = { + address: TEST_ACCOUNT_ADDRESS as Address, + description, + image: new File([imageFile], 'clabs.jpg', { type: 'image/jpeg' }), + interests, + name, + twitterUrl, + verificationUrl, + websiteUrl: websiteUrl as string, + } as RegisterDelegateRequest; + const signature = await walletClient.signTypedData({ ...EIP712Delegatee, message: { - address: TEST_ACCOUNT_ADDRESS, - name, - verificationUrl, + ...requestWithoutSignature, + imageSha: sha256(new Uint8Array(imageFile)), + websiteUrl: requestWithoutSignature.websiteUrl || '', + twitterUrl: requestWithoutSignature.twitterUrl || '', }, });