diff --git a/apps/shared/src/events/credentialOfferEvents.ts b/apps/shared/src/events/credentialOfferEvents.ts
index 63e9eb15e81cc97ef0c9a264226b5c893e21a497..0735939c6bc3d82685beb704be49b9ef5d156095 100644
--- a/apps/shared/src/events/credentialOfferEvents.ts
+++ b/apps/shared/src/events/credentialOfferEvents.ts
@@ -1,5 +1,9 @@
 import type { BaseEventInput } from './baseEvents.js';
-import type { CredentialExchangeRecord } from '@aries-framework/core';
+
+import {
+  CredentialExchangeRecord,
+  JsonTransformer,
+} from '@aries-framework/core';
 
 import { BaseEvent } from './baseEvents.js';
 
@@ -11,7 +15,9 @@ export class EventAnonCredsCredentialOfferGetAll extends BaseEvent<
   public static token = 'anoncreds.credentialOffers.getAll';
 
   public get instance() {
-    return this.data;
+    return this.data.map((d) =>
+      JsonTransformer.fromJSON(d, CredentialExchangeRecord),
+    );
   }
 
   public static fromEvent(e: EventAnonCredsCredentialOfferGetAll) {
@@ -33,7 +39,9 @@ export class EventAnonCredsCredentialOfferGetById extends BaseEvent<CredentialEx
   public static token = 'anoncreds.credentialOffers.getById';
 
   public get instance() {
-    return this.data;
+    return this.data
+      ? JsonTransformer.fromJSON(this.data, CredentialExchangeRecord)
+      : null;
   }
 
   public static fromEvent(e: EventAnonCredsCredentialOfferGetById) {
diff --git a/apps/shared/src/events/credentialRequestEvents.ts b/apps/shared/src/events/credentialRequestEvents.ts
index 9daefa1066c768641d71f5968464cd40ee9c9481..4039e655ee09cb08db7a23f8bc617f72fe9e487a 100644
--- a/apps/shared/src/events/credentialRequestEvents.ts
+++ b/apps/shared/src/events/credentialRequestEvents.ts
@@ -1,5 +1,9 @@
 import type { BaseEventInput } from './baseEvents.js';
-import type { CredentialExchangeRecord } from '@aries-framework/core';
+
+import {
+  CredentialExchangeRecord,
+  JsonTransformer,
+} from '@aries-framework/core';
 
 import { BaseEvent } from './baseEvents.js';
 
@@ -11,7 +15,9 @@ export class EventAnonCredsCredentialRequestGetAll extends BaseEvent<
   public static token = 'anoncreds.credentialRequests.getAll';
 
   public get instance() {
-    return this.data;
+    return this.data.map((d) =>
+      JsonTransformer.fromJSON(d, CredentialExchangeRecord),
+    );
   }
 
   public static fromEvent(e: EventAnonCredsCredentialRequestGetAll) {
@@ -33,7 +39,9 @@ export class EventAnonCredsCredentialRequestGetById extends BaseEvent<Credential
   public static token = 'anoncreds.credentialRequests.getById';
 
   public get instance() {
-    return this.data;
+    return this.data
+      ? JsonTransformer.fromJSON(this.data, CredentialExchangeRecord)
+      : null;
   }
 
   public static fromEvent(e: EventAnonCredsCredentialRequestGetById) {
diff --git a/apps/shared/src/events/didEvents.ts b/apps/shared/src/events/didEvents.ts
index e300545be74213c4172a34a216ecdd7eaf50c21a..1baaa030e86f81641442837db0e45697a0255f98 100644
--- a/apps/shared/src/events/didEvents.ts
+++ b/apps/shared/src/events/didEvents.ts
@@ -19,6 +19,11 @@ export class EventDidsResolve extends BaseEvent<DidDocument> {
 
 export type EventDidsRegisterIndyFromSeedInput = BaseEventInput<{
   seed: string;
+  services?: Array<{
+    identifier: string;
+    url: string;
+    type: string;
+  }>;
 }>;
 export class EventDidsRegisterIndyFromSeed extends BaseEvent<Array<string>> {
   public static token = 'dids.register.indy.fromSeed';
diff --git a/apps/shared/src/events/proofEvents.ts b/apps/shared/src/events/proofEvents.ts
new file mode 100644
index 0000000000000000000000000000000000000000..195777bd81c2776394480abb9a1223295476b71e
--- /dev/null
+++ b/apps/shared/src/events/proofEvents.ts
@@ -0,0 +1,108 @@
+import type { BaseEventInput } from './baseEvents.js';
+import type {
+  AnonCredsPredicateType,
+  AnonCredsProofRequestRestriction,
+} from '@aries-framework/anoncreds';
+
+import { JsonTransformer, ProofExchangeRecord } from '@aries-framework/core';
+
+import { BaseEvent } from './baseEvents.js';
+
+export type EventAnonCredsProofsGetAllInput = BaseEventInput;
+export class EventAnonCredsProofsGetAll extends BaseEvent<
+  Array<ProofExchangeRecord>
+> {
+  public static token = 'anoncreds.proofs.getAll';
+
+  public get instance() {
+    return this.data.map((d) =>
+      JsonTransformer.fromJSON(d, ProofExchangeRecord),
+    );
+  }
+
+  public static fromEvent(e: EventAnonCredsProofsGetAll) {
+    return new EventAnonCredsProofsGetAll(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventAnonCredsProofsGetByIdInput = BaseEventInput<{
+  proofRecordId: string;
+}>;
+export class EventAnonCredsProofsGetById extends BaseEvent<ProofExchangeRecord | null> {
+  public static token = 'anoncreds.proofs.getById';
+
+  public get instance() {
+    return this.data
+      ? JsonTransformer.fromJSON(this.data, ProofExchangeRecord)
+      : this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsProofsGetById) {
+    return new EventAnonCredsProofsGetById(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventDidcommAnonCredsProofsRequestInput = BaseEventInput<{
+  connectionId: string;
+  name: string;
+  requestedAttributes: {
+    [groupName: string]: {
+      names: Array<string>;
+      restrictions?: Array<AnonCredsProofRequestRestriction>;
+    };
+  };
+  requestedPredicates: {
+    [groupName: string]: {
+      name: string;
+      predicateType: AnonCredsPredicateType;
+      predicateValue: number;
+      restrictions?: Array<AnonCredsProofRequestRestriction>;
+    };
+  };
+}>;
+export class EventDidcommAnonCredsProofsRequest extends BaseEvent<ProofExchangeRecord> {
+  public static token = 'didcomm.anoncreds.proofs.request';
+
+  public get instance() {
+    return JsonTransformer.fromJSON(this.data, ProofExchangeRecord);
+  }
+
+  public static fromEvent(e: EventDidcommAnonCredsProofsRequest) {
+    return new EventDidcommAnonCredsProofsRequest(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventAnonCredsProofsDeleteByIdInput = BaseEventInput<{
+  proofRecordId: string;
+}>;
+export class EventAnonCredsProofsDeleteById extends BaseEvent {
+  public static token = 'anoncreds.proofs.deleteById';
+
+  public static fromEvent(e: EventDidcommAnonCredsProofsRequest) {
+    return new EventDidcommAnonCredsProofsRequest(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
diff --git a/apps/shared/src/index.ts b/apps/shared/src/index.ts
index 20c9faf786512da49c496a8d717ff643e430242b..36edf136298f8c16bbcaea2801132030521a0951 100644
--- a/apps/shared/src/index.ts
+++ b/apps/shared/src/index.ts
@@ -12,6 +12,7 @@ export * from './events/credentialDefinitionEvents.js';
 export * from './events/credentialEvents.js';
 export * from './events/credentialOfferEvents.js';
 export * from './events/credentialRequestEvents.js';
+export * from './events/proofEvents.js';
 
 export * from './dto/pagination-params.dto.js';
 export * from './dto/multitenancy-params.dto.js';
diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts
index b1d9bd9279c2175eae5386fe50ecdccc14996a9e..47fe62fbe89e34e0c1893640f495513633dc2ee3 100644
--- a/apps/ssi-abstraction/src/agent/agent.service.ts
+++ b/apps/ssi-abstraction/src/agent/agent.service.ts
@@ -6,7 +6,9 @@ import type { OnApplicationShutdown } from '@nestjs/common';
 import {
   AnonCredsCredentialFormatService,
   AnonCredsModule,
+  AnonCredsProofFormatService,
   LegacyIndyCredentialFormatService,
+  LegacyIndyProofFormatService,
 } from '@aries-framework/anoncreds';
 import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs';
 import { AskarModule } from '@aries-framework/askar';
@@ -23,11 +25,14 @@ import {
   LogLevel,
   PeerDidRegistrar,
   PeerDidResolver,
+  ProofsModule,
   V2CredentialProtocol,
+  V2ProofProtocol,
   WebDidResolver,
 } from '@aries-framework/core';
 import {
   IndyVdrAnonCredsRegistry,
+  IndyVdrIndyDidRegistrar,
   IndyVdrIndyDidResolver,
   IndyVdrModule,
   IndyVdrSovDidResolver,
@@ -90,13 +95,14 @@ export class AgentService implements OnApplicationShutdown {
   }
 
   public get modules() {
-    const { autoAcceptConnection, autoAcceptCredential } =
+    const { autoAcceptConnection, autoAcceptCredential, autoAcceptProof } =
       this.configService.get('agent');
 
     return {
       connections: new ConnectionsModule({
         autoAcceptConnections: autoAcceptConnection,
       }),
+
       credentials: new CredentialsModule({
         autoAcceptCredentials: autoAcceptCredential,
         credentialProtocols: [
@@ -109,6 +115,18 @@ export class AgentService implements OnApplicationShutdown {
         ],
       }),
 
+      proofs: new ProofsModule({
+        autoAcceptProofs: autoAcceptProof,
+        proofProtocols: [
+          new V2ProofProtocol({
+            proofFormats: [
+              new AnonCredsProofFormatService(),
+              new LegacyIndyProofFormatService(),
+            ],
+          }),
+        ],
+      }),
+
       anoncredsRs: new AnonCredsRsModule({ anoncreds }),
       anoncreds: new AnonCredsModule({
         registries: [new IndyVdrAnonCredsRegistry()],
@@ -128,6 +146,7 @@ export class AgentService implements OnApplicationShutdown {
           new PeerDidRegistrar(),
           new KeyDidRegistrar(),
           new JwkDidRegistrar(),
+          new IndyVdrIndyDidRegistrar()
         ],
       }),
 
diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts
index a434e597d68e243499ef5ff02e0f9bf3aa8ef1a5..af6a9933d3a77ff8a817ff679369b593e05dc9d9 100644
--- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts
+++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts
@@ -1,24 +1,20 @@
 import { Controller } from '@nestjs/common';
 import { MessagePattern } from '@nestjs/microservices';
 import {
+  EventAnonCredsCredentialOfferGetById,
+  EventAnonCredsCredentialOfferGetByIdInput,
+  EventAnonCredsCredentialRequestGetById,
+  EventAnonCredsCredentialRequestGetByIdInput,
+  EventAnonCredsCredentialsDeleteById,
+  EventAnonCredsCredentialsDeleteByIdInput,
   EventAnonCredsCredentialsGetAll,
   EventAnonCredsCredentialsGetAllInput,
   EventAnonCredsCredentialsGetById,
   EventAnonCredsCredentialsGetByIdInput,
   EventDidcommAnonCredsCredentialsOffer,
   EventDidcommAnonCredsCredentialsOfferInput,
-  EventDidcommAnonCredsCredentialsOfferToSelfInput,
   EventDidcommAnonCredsCredentialsOfferToSelf,
-  EventAnonCredsCredentialOfferGetAll,
-  EventAnonCredsCredentialOfferGetAllInput,
-  EventAnonCredsCredentialRequestGetAll,
-  EventAnonCredsCredentialRequestGetAllInput,
-  EventAnonCredsCredentialsDeleteById,
-  EventAnonCredsCredentialsDeleteByIdInput,
-  EventAnonCredsCredentialOfferGetById,
-  EventAnonCredsCredentialOfferGetByIdInput,
-  EventAnonCredsCredentialRequestGetById,
-  EventAnonCredsCredentialRequestGetByIdInput,
+  EventDidcommAnonCredsCredentialsOfferToSelfInput
 } from '@ocm/shared';
 
 import { AnonCredsCredentialsService } from './anoncredsCredentials.service.js';
@@ -37,26 +33,6 @@ export class AnonCredsCredentialsController {
     );
   }
 
-  @MessagePattern(EventAnonCredsCredentialOfferGetAll.token)
-  public async getAllOffers(
-    options: EventAnonCredsCredentialOfferGetAllInput,
-  ): Promise<EventAnonCredsCredentialOfferGetAll> {
-    return new EventAnonCredsCredentialOfferGetAll(
-      await this.credentialsService.getAllOffers(options),
-      options.tenantId,
-    );
-  }
-
-  @MessagePattern(EventAnonCredsCredentialRequestGetAll.token)
-  public async getAllRequests(
-    options: EventAnonCredsCredentialRequestGetAllInput,
-  ): Promise<EventAnonCredsCredentialRequestGetAll> {
-    return new EventAnonCredsCredentialRequestGetAll(
-      await this.credentialsService.getAllRequests(options),
-      options.tenantId,
-    );
-  }
-
   @MessagePattern(EventAnonCredsCredentialsGetById.token)
   public async getById(
     options: EventAnonCredsCredentialsGetByIdInput,
diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts
index a1aa04b633c5bb454e2cb7e72b81f4935e48d586..94617f16d8574e662cbbc55699b4e07aaa2f0a8a 100644
--- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts
+++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts
@@ -1,22 +1,21 @@
+import type { CredentialExchangeRecord} from '@aries-framework/core';
 import type {
-  EventAnonCredsCredentialRequestGetByIdInput,
+  EventAnonCredsCredentialOfferGetAll,
   EventAnonCredsCredentialOfferGetAllInput,
   EventAnonCredsCredentialOfferGetById,
   EventAnonCredsCredentialOfferGetByIdInput,
+  EventAnonCredsCredentialRequestGetAll,
   EventAnonCredsCredentialRequestGetAllInput,
+  EventAnonCredsCredentialRequestGetById,
+  EventAnonCredsCredentialRequestGetByIdInput,
+  EventAnonCredsCredentialsDeleteById,
+  EventAnonCredsCredentialsDeleteByIdInput,
   EventAnonCredsCredentialsGetAllInput,
   EventAnonCredsCredentialsGetByIdInput,
-  EventDidcommAnonCredsCredentialsOfferInput,
-  EventDidcommAnonCredsCredentialsOfferToSelfInput,
-  EventAnonCredsCredentialRequestGetById,
   EventDidcommAnonCredsCredentialsOffer,
+  EventDidcommAnonCredsCredentialsOfferInput,
   EventDidcommAnonCredsCredentialsOfferToSelf,
-  EventAnonCredsCredentialsGetById,
-  EventAnonCredsCredentialRequestGetAll,
-  EventAnonCredsCredentialOfferGetAll,
-  EventAnonCredsCredentialsGetAll,
-  EventAnonCredsCredentialsDeleteByIdInput,
-  EventAnonCredsCredentialsDeleteById,
+  EventDidcommAnonCredsCredentialsOfferToSelfInput
 } from '@ocm/shared';
 
 import { AutoAcceptCredential, CredentialState } from '@aries-framework/core';
@@ -33,7 +32,7 @@ export class AnonCredsCredentialsService {
   public async getAll({
     tenantId,
   }: EventAnonCredsCredentialsGetAllInput): Promise<
-    EventAnonCredsCredentialsGetAll['data']
+    Array<CredentialExchangeRecord>
   > {
     return this.withTenantService.invoke(tenantId, (t) =>
       t.credentials.getAll(),
@@ -85,9 +84,7 @@ export class AnonCredsCredentialsService {
   public async getById({
     tenantId,
     credentialRecordId,
-  }: EventAnonCredsCredentialsGetByIdInput): Promise<
-    EventAnonCredsCredentialsGetById['data']
-  > {
+  }: EventAnonCredsCredentialsGetByIdInput): Promise<CredentialExchangeRecord | null> {
     return this.withTenantService.invoke(tenantId, (t) =>
       t.credentials.findById(credentialRecordId),
     );
diff --git a/apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts b/apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..91eb60562a79e398f61630432b4c6628ea053a40
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts
@@ -0,0 +1,77 @@
+import { ProofExchangeRecord, ProofState } from '@aries-framework/core';
+import { Test } from '@nestjs/testing';
+
+import { mockConfigModule } from '../../../config/__tests__/mockConfig.js';
+import { AgentModule } from '../../agent.module.js';
+import { AnonCredsProofsController } from '../anoncredsProofs.controller.js';
+import { AnonCredsProofsService } from '../anoncredsProofs.service.js';
+
+describe('AnonCredsProofsController', () => {
+  let proofsController: AnonCredsProofsController;
+  let proofsService: AnonCredsProofsService;
+
+  beforeEach(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [mockConfigModule(), AgentModule],
+      controllers: [AnonCredsProofsController],
+      providers: [AnonCredsProofsService],
+    }).compile();
+
+    proofsService = moduleRef.get(AnonCredsProofsService);
+    proofsController = moduleRef.get(AnonCredsProofsController);
+  });
+
+  it('get all', async () => {
+    const result: Array<ProofExchangeRecord> = [];
+    jest.spyOn(proofsService, 'getAll').mockResolvedValue(result);
+
+    const event = await proofsController.getAll({
+      tenantId: 'some-id',
+    });
+
+    expect(event.data).toStrictEqual(result);
+  });
+
+  it('get by id', async () => {
+    const result: ProofExchangeRecord | null = null;
+    jest.spyOn(proofsService, 'getById').mockResolvedValue(result);
+
+    const event = await proofsController.getById({
+      tenantId: 'some-id',
+      proofRecordId: 'some-id',
+    });
+
+    expect(event.data).toStrictEqual(result);
+  });
+
+  it('request', async () => {
+    const result = new ProofExchangeRecord({
+      state: ProofState.Done,
+      threadId: 'some-id',
+      protocolVersion: 'v2',
+    });
+    jest.spyOn(proofsService, 'request').mockResolvedValue(result);
+
+    const event = await proofsController.request({
+      tenantId: 'some-id',
+      connectionId: 'some-id',
+      name: 'My New Proof Request',
+      requestedAttributes: {
+        identity: {
+          names: ['name'],
+          restrictions: [{ issuer_id: 'did:web:government.org' }],
+        },
+      },
+      requestedPredicates: {
+        'age > 18': {
+          name: 'age',
+          restrictions: [{ issuer_id: 'did:web:government.org' }],
+          predicateType: '>',
+          predicateValue: 18,
+        },
+      },
+    });
+
+    expect(event.data).toStrictEqual(result);
+  });
+});
diff --git a/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.controller.ts b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c29a03defeba3e94702a26aac505c0fe3bb3b7e0
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.controller.ts
@@ -0,0 +1,59 @@
+import { Controller } from '@nestjs/common';
+import { MessagePattern } from '@nestjs/microservices';
+import {
+  EventAnonCredsProofsDeleteById,
+  EventAnonCredsProofsDeleteByIdInput,
+  EventAnonCredsProofsGetAll,
+  EventAnonCredsProofsGetAllInput,
+  EventAnonCredsProofsGetById,
+  EventAnonCredsProofsGetByIdInput,
+  EventDidcommAnonCredsProofsRequest,
+  EventDidcommAnonCredsProofsRequestInput,
+} from '@ocm/shared';
+
+import { AnonCredsProofsService } from './anoncredsProofs.service.js';
+
+@Controller('anoncredsProofs')
+export class AnonCredsProofsController {
+  public constructor(private proofsService: AnonCredsProofsService) {}
+
+  @MessagePattern(EventAnonCredsProofsGetAll.token)
+  public async getAll(
+    options: EventAnonCredsProofsGetAllInput,
+  ): Promise<EventAnonCredsProofsGetAll> {
+    return new EventAnonCredsProofsGetAll(
+      await this.proofsService.getAll(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventAnonCredsProofsGetById.token)
+  public async getById(
+    options: EventAnonCredsProofsGetByIdInput,
+  ): Promise<EventAnonCredsProofsGetById> {
+    return new EventAnonCredsProofsGetById(
+      await this.proofsService.getById(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventAnonCredsProofsDeleteById.token)
+  public async deleteById(
+    options: EventAnonCredsProofsDeleteByIdInput,
+  ): Promise<EventAnonCredsProofsDeleteById> {
+    return new EventAnonCredsProofsDeleteById(
+      await this.proofsService.deleteById(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventDidcommAnonCredsProofsRequest.token)
+  public async request(
+    options: EventDidcommAnonCredsProofsRequestInput,
+  ): Promise<EventDidcommAnonCredsProofsRequest> {
+    return new EventDidcommAnonCredsProofsRequest(
+      await this.proofsService.request(options),
+      options.tenantId,
+    );
+  }
+}
diff --git a/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.module.ts b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ab1719ba2aff98122bf50ede7538dde777c3615
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+
+import { AgentModule } from '../agent.module.js';
+
+import { AnonCredsProofsController } from './anoncredsProofs.controller.js';
+import { AnonCredsProofsService } from './anoncredsProofs.service.js';
+
+@Module({
+  imports: [AgentModule],
+  providers: [AnonCredsProofsService],
+  controllers: [AnonCredsProofsController],
+})
+export class AnonCredsProofsModule {}
diff --git a/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6de76335a02b03d4221795d498357d7d615eb4a5
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts
@@ -0,0 +1,88 @@
+import type {
+  EventAnonCredsProofsDeleteById,
+  EventAnonCredsProofsDeleteByIdInput,
+  EventAnonCredsProofsGetAll,
+  EventAnonCredsProofsGetAllInput,
+  EventAnonCredsProofsGetById,
+  EventAnonCredsProofsGetByIdInput,
+  EventDidcommAnonCredsProofsRequest,
+  EventDidcommAnonCredsProofsRequestInput,
+} from '@ocm/shared';
+
+import { Injectable } from '@nestjs/common';
+
+import { WithTenantService } from '../withTenantService.js';
+
+@Injectable()
+export class AnonCredsProofsService {
+  public constructor(private withTenantService: WithTenantService) {}
+
+  public async getAll({
+    tenantId,
+  }: EventAnonCredsProofsGetAllInput): Promise<
+    EventAnonCredsProofsGetAll['data']
+  > {
+    return this.withTenantService.invoke(tenantId, (t) => t.proofs.getAll());
+  }
+
+  public async getById({
+    tenantId,
+    proofRecordId,
+  }: EventAnonCredsProofsGetByIdInput): Promise<
+    EventAnonCredsProofsGetById['data']
+  > {
+    return this.withTenantService.invoke(tenantId, (t) =>
+      t.proofs.findById(proofRecordId),
+    );
+  }
+
+  public async deleteById({
+    tenantId,
+    proofRecordId,
+  }: EventAnonCredsProofsDeleteByIdInput): Promise<
+    EventAnonCredsProofsDeleteById['data']
+  > {
+    return this.withTenantService.invoke(tenantId, async (t) => {
+      await t.proofs.deleteById(proofRecordId);
+      return {};
+    });
+  }
+
+  public async request({
+    tenantId,
+    connectionId,
+    name,
+    requestedAttributes,
+    requestedPredicates,
+  }: EventDidcommAnonCredsProofsRequestInput): Promise<
+    EventDidcommAnonCredsProofsRequest['data']
+  > {
+    const transformedPredicates = Object.entries(requestedPredicates).reduce(
+      (prev, [key, value]) => ({
+        ...prev,
+        [key]: {
+          name: value.name,
+          restrictions: value.restrictions,
+          p_type: value.predicateType,
+          p_value: value.predicateValue,
+        },
+      }),
+      {},
+    );
+
+    return this.withTenantService.invoke(tenantId, (t) =>
+      t.proofs.requestProof({
+        connectionId,
+        protocolVersion: 'v2',
+        proofFormats: {
+          anoncreds: {
+            name,
+            version: '1.0',
+            requested_attributes: requestedAttributes,
+            requested_predicates: transformedPredicates,
+          },
+        },
+      }),
+    );
+  }
+}
diff --git a/apps/ssi-abstraction/src/agent/dids/dids.service.ts b/apps/ssi-abstraction/src/agent/dids/dids.service.ts
index 7b0ad1ec1682b95964a3e21d9ae619bca4787478..76cd0a8b0a8f1b0ae1f623cc704502774485032c 100644
--- a/apps/ssi-abstraction/src/agent/dids/dids.service.ts
+++ b/apps/ssi-abstraction/src/agent/dids/dids.service.ts
@@ -1,6 +1,21 @@
-import type { EventDidsResolveInput } from '@ocm/shared';
+import type { LEDGERS } from '../../config/ledger.js';
+import type {
+  IndyVdrDidCreateOptions,
+  IndyVdrDidCreateResult,
+} from '@aries-framework/indy-vdr';
+import type {
+  EventDidsRegisterIndyFromSeed,
+  EventDidsRegisterIndyFromSeedInput,
+  EventDidsResolve,
+  EventDidsResolveInput,
+} from '@ocm/shared';
 
-import { KeyType, TypedArrayEncoder } from '@aries-framework/core';
+import {
+  DidDocumentService,
+  Hasher,
+  KeyType,
+  TypedArrayEncoder,
+} from '@aries-framework/core';
 import { Injectable } from '@nestjs/common';
 import { ConfigService } from '@nestjs/config';
 
@@ -14,7 +29,10 @@ export class DidsService {
     private configService: ConfigService,
   ) {}
 
-  public async resolve({ did, tenantId }: EventDidsResolveInput) {
+  public async resolve({
+    did,
+    tenantId,
+  }: EventDidsResolveInput): Promise<EventDidsResolve['data']> {
     return this.withTenantService.invoke(tenantId, async (t) => {
       const {
         didDocument,
@@ -36,32 +54,80 @@ export class DidsService {
   public async registerDidIndyFromSeed({
     tenantId,
     seed,
-  }: {
-    tenantId: string;
-    seed: string;
-  }): Promise<Array<string>> {
-    const ledgerIds = this.configService.get('agent.ledgerIds');
+    services,
+  }: EventDidsRegisterIndyFromSeedInput): Promise<
+    EventDidsRegisterIndyFromSeed['data']
+  > {
+    const dids: Array<string> = [];
+
+    const ledgerIds = this.configService.get('agent.ledgerIds') as Array<
+      keyof typeof LEDGERS
+    >;
+
+    const publicDidSeed = this.configService.get(
+      'agent.publicDidSeed',
+    ) as string;
 
-    const registeredPublicDidResponses = await registerPublicDids({
+    const publicDids = await registerPublicDids({
       ledgerIds,
-      seed,
+      seed: publicDidSeed,
     });
 
-    for (const publicDidResponse of registeredPublicDidResponses) {
-      await this.withTenantService.invoke(tenantId, (t) =>
-        t.dids.import({
-          overwrite: true,
-          did: publicDidResponse.did,
-          privateKeys: [
-            {
-              keyType: KeyType.Ed25519,
-              privateKey: TypedArrayEncoder.fromString(seed),
-            },
-          ],
+    const { publicKey, publicKeyBase58 } = await this.withTenantService.invoke(
+      tenantId,
+      async (t) =>
+        t.wallet.createKey({
+          privateKey: TypedArrayEncoder.fromString(seed),
+          keyType: KeyType.Ed25519,
         }),
-      );
+    );
+
+    await this.withTenantService.invoke(tenantId, async (t) =>
+      t.wallet.createKey({
+        privateKey: TypedArrayEncoder.fromString(publicDidSeed),
+        keyType: KeyType.Ed25519,
+      }),
+    );
+
+    const buffer = Hasher.hash(publicKey, 'sha2-256');
+    const id = TypedArrayEncoder.toBase58(buffer.slice(0, 16));
+    const verkey = publicKeyBase58;
+
+    for (const publicDid of publicDids) {
+      const did = `did:indy:${publicDid.namespace}:${id}`;
+      const didDocumentServices: Array<DidDocumentService> | undefined =
+        services?.map(
+          (s) =>
+            new DidDocumentService({
+              id: `${did}#${s.identifier}`,
+              type: s.type,
+              serviceEndpoint: s.url,
+            }),
+        );
+
+
+      await this.withTenantService.invoke(tenantId, async (t) => {
+        const result = (await t.dids.create<IndyVdrDidCreateOptions>({
+          did,
+          options: {
+            verkey,
+            endorserMode: 'internal',
+            endorserDid: publicDid.did,
+            services: didDocumentServices,
+            useEndpointAttrib: true,
+          },
+        })) as IndyVdrDidCreateResult;
+
+        if (result.didState.state !== 'finished') {
+          throw Error(
+            `An error occurred while trying to register the did: '${did}'. Result: ${JSON.stringify(result)}`,
+          );
+        }
+
+        dids.push(result.didState.did);
+      });
     }
 
-    return registeredPublicDidResponses.map((r) => r.did);
+    return dids;
   }
 }
diff --git a/apps/ssi-abstraction/src/agent/ledger/register.ts b/apps/ssi-abstraction/src/agent/ledger/register.ts
index 8600a454b48e609e2798aba6482cbd719720c419..b7127ff680ee0fd031b657a3a16167602bab3bd7 100644
--- a/apps/ssi-abstraction/src/agent/ledger/register.ts
+++ b/apps/ssi-abstraction/src/agent/ledger/register.ts
@@ -11,14 +11,17 @@ type RegisterPublicDidOptions = {
 };
 
 type LedgerRegistrationBody = {
-  role?: 'ENDORSER';
-  seed: string;
+  role: 'ENDORSER';
+  seed?: string;
+  did?: string;
+  verkey?: string;
 };
 
 type RegisterPublicDidResponse = {
   seed: string;
   did: string;
   verkey: string;
+  namespace: string;
 };
 
 export const registerPublicDids = async ({
@@ -45,7 +48,7 @@ export const registerPublicDids = async ({
       if (res.data) {
         logger.info('Agent DID registered.');
         res.data.did = `did:indy:${ledgerNamespace}:${res.data.did}`;
-        responses.push(res.data);
+        responses.push({ ...res.data, namespace: ledgerNamespace });
       } else {
         throw new Error('No data was returned from the ledger request');
       }
diff --git a/apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts b/apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe6c5f7bf768c38d421e5514558879d4f8e8b625
--- /dev/null
+++ b/apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts
@@ -0,0 +1,133 @@
+import type { INestApplication } from '@nestjs/common';
+import type { ClientProxy } from '@nestjs/microservices';
+import type {
+  EventAnonCredsProofsGetAllInput,
+  EventAnonCredsProofsGetByIdInput,
+  EventDidcommAnonCredsProofsRequestInput,
+} from '@ocm/shared';
+
+import { ProofState } from '@aries-framework/core';
+import { ClientsModule, Transport } from '@nestjs/microservices';
+import { Test } from '@nestjs/testing';
+import {
+  EventAnonCredsProofsGetAll,
+  EventAnonCredsProofsGetById,
+  EventDidcommAnonCredsProofsRequest,
+} from '@ocm/shared';
+import { firstValueFrom } from 'rxjs';
+
+import { AgentModule } from '../src/agent/agent.module.js';
+import { AnonCredsProofsModule } from '../src/agent/anoncredsProofs/anoncredsProofs.module.js';
+import { ConnectionsModule } from '../src/agent/connections/connections.module.js';
+import { ConnectionsService } from '../src/agent/connections/connections.service.js';
+import { DidsModule } from '../src/agent/dids/dids.module.js';
+import { TenantsModule } from '../src/agent/tenants/tenants.module.js';
+import { TenantsService } from '../src/agent/tenants/tenants.service.js';
+import { mockConfigModule } from '../src/config/__tests__/mockConfig.js';
+
+describe('Proofs', () => {
+  const TOKEN = 'PROOFS_CLIENT_SERVICE';
+  let app: INestApplication;
+  let client: ClientProxy;
+  let tenantId: string;
+
+  let connectionId: string;
+  let credentialDefinitionId: string;
+
+  beforeAll(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [
+        mockConfigModule(3004, true),
+        AgentModule,
+        ConnectionsModule,
+        AnonCredsProofsModule,
+        TenantsModule,
+        DidsModule,
+        ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]),
+      ],
+    }).compile();
+
+    app = moduleRef.createNestApplication();
+
+    app.connectMicroservice({ transport: Transport.NATS });
+
+    await app.startAllMicroservices();
+    await app.init();
+
+    client = app.get(TOKEN);
+    await client.connect();
+
+    const tenantsService = app.get(TenantsService);
+    const { id: tId } = await tenantsService.create(TOKEN);
+    tenantId = tId;
+
+    const connectionsService = app.get(ConnectionsService);
+    const { id } = await connectionsService.createConnectionWithSelf({
+      tenantId,
+    });
+    connectionId = id;
+  });
+
+  afterAll(async () => {
+    await app.close();
+    client.close();
+  });
+
+  it(EventAnonCredsProofsGetAll.token, async () => {
+    const response$ = client.send<
+      EventAnonCredsProofsGetAll,
+      EventAnonCredsProofsGetAllInput
+    >(EventAnonCredsProofsGetAll.token, { tenantId });
+    const response = await firstValueFrom(response$);
+    const eventInstance = EventAnonCredsProofsGetAll.fromEvent(response);
+
+    expect(eventInstance.instance).toEqual(expect.arrayContaining([]));
+  });
+
+  it(EventAnonCredsProofsGetById.token, async () => {
+    const response$ = client.send<
+      EventAnonCredsProofsGetById,
+      EventAnonCredsProofsGetByIdInput
+    >(EventAnonCredsProofsGetById.token, {
+      tenantId,
+      proofRecordId: 'some-id',
+    });
+    const response = await firstValueFrom(response$);
+    const eventInstance = EventAnonCredsProofsGetById.fromEvent(response);
+
+    expect(eventInstance.instance).toEqual(null);
+  });
+
+  it(EventDidcommAnonCredsProofsRequest.token, async () => {
+    const response$ = client.send<
+      EventDidcommAnonCredsProofsRequest,
+      EventDidcommAnonCredsProofsRequestInput
+    >(EventDidcommAnonCredsProofsRequest.token, {
+      tenantId,
+      name: 'My Test Proof Request',
+      connectionId,
+      requestedAttributes: {
+        Identity: {
+          names: ['Name'],
+          restrictions: [{ cred_def_id: credentialDefinitionId }],
+        },
+      },
+      requestedPredicates: {
+        'Age > 21': {
+          name: 'Age',
+          restrictions: [{ cred_def_id: credentialDefinitionId }],
+          predicateType: '>',
+          predicateValue: 21,
+        },
+      },
+    });
+
+    const response = await firstValueFrom(response$);
+    const eventInstance =
+      EventDidcommAnonCredsProofsRequest.fromEvent(response);
+
+    expect(eventInstance.instance).toMatchObject({
+      state: ProofState.RequestSent,
+    });
+  });
+});
diff --git a/apps/ssi-abstraction/test/dids.e2e-spec.ts b/apps/ssi-abstraction/test/dids.e2e-spec.ts
index 52c8a7b66caaba8d59e7d2c64d98ce725e977532..2cbb580fd7142d239214c5338c9b0ff33ef5a63b 100644
--- a/apps/ssi-abstraction/test/dids.e2e-spec.ts
+++ b/apps/ssi-abstraction/test/dids.e2e-spec.ts
@@ -8,6 +8,7 @@ import type {
 import { ClientsModule, Transport } from '@nestjs/microservices';
 import { Test } from '@nestjs/testing';
 import { EventDidsRegisterIndyFromSeed, EventDidsResolve } from '@ocm/shared';
+import { randomBytes } from 'crypto';
 import { firstValueFrom } from 'rxjs';
 
 import { AgentModule } from '../src/agent/agent.module.js';
@@ -58,16 +59,23 @@ describe('Dids', () => {
       EventDidsRegisterIndyFromSeed,
       EventDidsRegisterIndyFromSeedInput
     >(EventDidsRegisterIndyFromSeed.token, {
-      seed: '12312367897123300000000000000000',
+      seed: randomBytes(16).toString('hex'),
       tenantId,
+      services: [
+        {
+          url: 'https://example.org',
+          type: 'endpoint',
+          identifier: 'endpoint',
+        },
+      ],
     });
 
     const response = await firstValueFrom(response$);
     const eventInstance = EventDidsRegisterIndyFromSeed.fromEvent(response);
 
-    expect(eventInstance.instance).toMatchObject(
-      expect.arrayContaining(['did:indy:bcovrin:test:9MMeff63VnCpogD2FWfKnJ']),
-    );
+    expect(
+      eventInstance.instance[0].startsWith('did:indy:bcovrin:test:'),
+    ).toBeTruthy();
   });
 
   it(EventDidsResolve.token, async () => {