diff --git a/src/lib/entities.ts b/src/lib/entities.ts index e35dcbf4..78524d33 100644 --- a/src/lib/entities.ts +++ b/src/lib/entities.ts @@ -1,10 +1,11 @@ -import { ethers } from 'ethers' -import { Authenticator, AuthChain } from '@dcl/crypto' +import { Authenticator, AuthIdentity } from '@dcl/crypto' import { Entity, EntityType } from '@dcl/schemas/dist/platform/entity' -import { ContentClient, createContentClient } from 'dcl-catalyst-client/dist/client/ContentClient' +import { + ContentClient, + createContentClient +} from 'dcl-catalyst-client/dist/client/ContentClient' import { BuildEntityWithoutFilesOptions } from 'dcl-catalyst-client/dist/client/types' import { buildEntityWithoutNewFiles } from 'dcl-catalyst-client/dist/client/utils/DeploymentBuilder' -import { getConnectedProvider } from './eth' import { ProfileEntity } from './types' import { PeerAPI } from './peer' import { createFetchComponent } from '@well-known-components/fetch-component' @@ -15,38 +16,23 @@ export class EntitiesOperator { private catalystContentClientWithoutGbCollector: ContentClient | null // Undefined until initialization private readonly peerAPI: PeerAPI - constructor(private peerUrl: string, private peerWithNoGbCollectorUrl?: string) { - this.catalystContentClient = createContentClient({ url: `${peerUrl}/content`, fetcher: createFetchComponent() }) + constructor( + private peerUrl: string, + private peerWithNoGbCollectorUrl?: string + ) { + this.catalystContentClient = createContentClient({ + url: `${peerUrl}/content`, + fetcher: createFetchComponent() + }) this.catalystContentClientWithoutGbCollector = peerWithNoGbCollectorUrl - ? createContentClient({ url: `${peerUrl}/content`, fetcher: createFetchComponent() }) + ? createContentClient({ + url: `${peerUrl}/content`, + fetcher: createFetchComponent() + }) : null this.peerAPI = new PeerAPI(peerUrl) } - /** - * Uses the provider to request the user for a signature to - * deploy an entity. - * - * @param address - The address of the deployer of the entity. - * @param entityId - The entity id that it's going to be deployed. - */ - private async authenticateEntityDeployment( - address: string, - entityId: string - ): Promise { - const provider = await getConnectedProvider() - if (!provider) - throw new Error( - "The provider couldn't be retrieved when creating the auth chain" - ) - const eth = new ethers.providers.Web3Provider(provider) - - const personal = eth.getSigner(address) - const signature = await personal.signMessage(entityId) - - return Authenticator.createSimpleAuthChain(entityId, address, signature) - } - /** * Gets the first {@link ProfileEntity} out of multiple possible profile entities or * returns the last one in case the given address has no profile entities. @@ -80,7 +66,7 @@ export class EntitiesOperator { hashesByKey: Map, entityType: EntityType, pointer: string, - address: string + identity: AuthIdentity ): Promise { const options: BuildEntityWithoutFilesOptions = { type: entityType, @@ -90,13 +76,17 @@ export class EntitiesOperator { timestamp: Date.now() } - const catalystContentClient = this.catalystContentClientWithoutGbCollector ?? this.catalystContentClient + const catalystContentClient = + this.catalystContentClientWithoutGbCollector ?? this.catalystContentClient const contentUrl = this.peerWithNoGbCollectorUrl ?? this.peerUrl - const entityToDeploy = await buildEntityWithoutNewFiles(createFetchComponent(), { contentUrl: `${contentUrl}/content`, ...options }) + const entityToDeploy = await buildEntityWithoutNewFiles( + createFetchComponent(), + { contentUrl: `${contentUrl}/content`, ...options } + ) - const authChain: AuthChain = await this.authenticateEntityDeployment( - address, + const authChain = Authenticator.signPayload( + identity, entityToDeploy.entityId ) diff --git a/src/modules/profile/sagas.spec.ts b/src/modules/profile/sagas.spec.ts index 223a55cc..2f13b741 100644 --- a/src/modules/profile/sagas.spec.ts +++ b/src/modules/profile/sagas.spec.ts @@ -1,3 +1,4 @@ +import { AuthIdentity } from '@dcl/crypto' import { Avatar } from '@dcl/schemas/dist/platform/profile' import { EntityType } from '@dcl/schemas/dist/platform/entity' import { expectSaga } from 'redux-saga-test-plan' @@ -7,7 +8,7 @@ import { profileFromLambda } from '../../tests/profileMocks' import { ProfileEntity } from '../../lib/types' import { PeerAPI } from '../../lib/peer' import { dynamicDeepParametersEquality } from '../../tests/sagas' -import { createProfileSaga } from './sagas' +import { NO_IDENTITY_FOUND_ERROR_MESSAGE, createProfileSaga } from './sagas' import { getHashesByKeyMap, lambdaProfileToContentProfile } from './utils' import { setProfileAvatarAliasFailure, @@ -18,7 +19,12 @@ import { setProfileAvatarDescriptionSuccess } from './actions' -const profileSagas = createProfileSaga({ peerUrl: 'aURL' }); +let mockAuthIdentity: AuthIdentity | undefined = {} as AuthIdentity + +const profileSagas = createProfileSaga({ + getIdentity: () => mockAuthIdentity, + peerUrl: 'aURL' +}) const address = 'anAddress' const description = 'aDescription' const errorMessage = 'anError' @@ -94,7 +100,7 @@ describe('when handling the action to set the profile avatar description', () => getHashesByKeyMap(newAvatar), EntityType.PROFILE, address, - address + mockAuthIdentity ], Promise.resolve(undefined) ) @@ -130,6 +136,32 @@ describe('when handling the action to set the profile avatar alias', () => { }) }) + describe('when there is no identity available', () => { + beforeEach(() => { + mockAuthIdentity = undefined + }) + afterAll(() => { + mockAuthIdentity = {} as AuthIdentity + }) + it('should dispatch an action to signal that the request failed', () => { + return expectSaga(profileSagas) + .provide([ + [ + matchers.call.fn(PeerAPI.prototype.fetchProfile), + dynamicDeepParametersEquality( + [address, { useCache: false }], + Promise.resolve(profileFromLambda) + ) + ] + ]) + .put( + setProfileAvatarAliasFailure(address, NO_IDENTITY_FOUND_ERROR_MESSAGE) + ) + .dispatch(setProfileAvatarAliasRequest(address, alias)) + .silentRun() + }) + }) + describe('when deploying the entity fails', () => { it('should dispatch an action to signal that the request failed', () => { return expectSaga(profileSagas) @@ -186,7 +218,7 @@ describe('when handling the action to set the profile avatar alias', () => { getHashesByKeyMap(newAvatar), EntityType.PROFILE, address, - address + mockAuthIdentity ], Promise.resolve(undefined) ) diff --git a/src/modules/profile/sagas.ts b/src/modules/profile/sagas.ts index c8aca68d..e367a980 100644 --- a/src/modules/profile/sagas.ts +++ b/src/modules/profile/sagas.ts @@ -1,4 +1,5 @@ import { takeLatest, put, call, takeEvery } from 'redux-saga/effects' +import { AuthIdentity } from '@dcl/crypto' import { Avatar } from '@dcl/schemas/dist/platform/profile' import { EntityType } from '@dcl/schemas/dist/platform/entity' import { PeerAPI } from '../../lib/peer' @@ -27,12 +28,16 @@ import { import { getHashesByKeyMap, lambdaProfileToContentProfile } from './utils' import { Profile } from './types' +export const NO_IDENTITY_FOUND_ERROR_MESSAGE = 'No identity found' + type CreateProfileSagaOptions = { + getIdentity: () => AuthIdentity | undefined peerUrl: string peerWithNoGbCollectorUrl?: string } export function createProfileSaga({ + getIdentity, peerUrl, peerWithNoGbCollectorUrl }: CreateProfileSagaOptions) { @@ -134,15 +139,20 @@ export function createProfileSaga({ const profileMetadata: Profile = { avatars: [newAvatar, ...profileWithContentHashes.avatars.slice(1)] } - - yield call( - [entities, 'deployEntityWithoutNewFiles'], - profileMetadata, - getHashesByKeyMap(newAvatar), - EntityType.PROFILE, - address, - address - ) + const identity = getIdentity() + + if (identity) { + yield call( + [entities, 'deployEntityWithoutNewFiles'], + profileMetadata, + getHashesByKeyMap(newAvatar), + EntityType.PROFILE, + address, + identity + ) + } else { + throw new Error(NO_IDENTITY_FOUND_ERROR_MESSAGE) + } return newAvatar }