Skip to content

Commit

Permalink
feat: onramp to solo wallet (#37)
Browse files Browse the repository at this point in the history
* feat: solowallet api

* chore: bump africastalking

* fix: bootstrap solowallet

* refactor: use shared transaction state definition

* feat: solowallet depends on swap service

* feat: onramp to solowallet
  • Loading branch information
okjodom authored Nov 29, 2024
1 parent 0c8ddf1 commit fc71ef4
Show file tree
Hide file tree
Showing 25 changed files with 382 additions and 60 deletions.
19 changes: 19 additions & 0 deletions apps/api/src/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
NOSTR_SERVICE_NAME,
SHARES_SERVICE_NAME,
SMS_SERVICE_NAME,
SOLOWALLET_SERVICE_NAME,
SWAP_SERVICE_NAME,
} from '@bitsacco/common';
import { SwapController, SwapService } from './swap';
Expand All @@ -19,6 +20,8 @@ import { SharesService } from './shares/shares.service';
import { SharesController } from './shares/shares.controller';
import { AdminController } from './admin/admin.controller';
import { AdminService } from './admin/admin.service';
import { SolowalletService } from './solowallet/solowallet.service';
import { SolowalletController } from './solowallet/solowallet.controller';

@Module({
imports: [
Expand All @@ -31,6 +34,8 @@ import { AdminService } from './admin/admin.service';
SWAP_GRPC_URL: Joi.string().required(),
NOSTR_GRPC_URL: Joi.string().required(),
SMS_GRPC_URL: Joi.string().required(),
SHARES_GRPC_URL: Joi.string().required(),
SOLOWALLET_GRPC_URL: Joi.string().required(),
REDIS_HOST: Joi.string().required(),
REDIS_PORT: Joi.number().required(),
}),
Expand Down Expand Up @@ -84,6 +89,18 @@ import { AdminService } from './admin/admin.service';
}),
inject: [ConfigService],
},
{
name: SOLOWALLET_SERVICE_NAME,
useFactory: (configService: ConfigService) => ({
transport: Transport.GRPC,
options: {
package: 'solowallet',
protoPath: join(__dirname, '../../../proto/solowallet.proto'),
url: configService.getOrThrow<string>('SOLOWALLET_GRPC_URL'),
},
}),
inject: [ConfigService],
},
{
name: EVENTS_SERVICE_BUS,
useFactory: (configService: ConfigService) => ({
Expand All @@ -103,13 +120,15 @@ import { AdminService } from './admin/admin.service';
NostrController,
SmsController,
SharesController,
SolowalletController,
],
providers: [
SwapService,
NostrService,
SmsService,
SharesService,
AdminService,
SolowalletService,
],
})
export class ApiModule {}
4 changes: 2 additions & 2 deletions apps/api/src/shares/shares.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import {
Empty,
GetShareDetailDto,
} from '@bitsacco/common';
import { Inject, Injectable } from '@nestjs/common';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { type ClientGrpc } from '@nestjs/microservices';

@Injectable()
export class SharesService {
export class SharesService implements OnModuleInit {
private client: SharesServiceClient;

constructor(@Inject(SHARES_SERVICE_NAME) private readonly grpc: ClientGrpc) {}
Expand Down
31 changes: 31 additions & 0 deletions apps/api/src/solowallet/solowallet.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { TestingModule } from '@nestjs/testing';
import { createTestingModuleWithValidation } from '@bitsacco/testing';
import { SolowalletController } from './solowallet.controller';
import { SolowalletService } from './solowallet.service';

describe('SolowalletController', () => {
let controller: SolowalletController;
let walletService: SolowalletService;

beforeEach(async () => {
const module: TestingModule = await createTestingModuleWithValidation({
controllers: [SolowalletController],
providers: [
{
provide: SolowalletService,
useValue: {
depositFunds: jest.fn(),
},
},
],
});

controller = module.get<SolowalletController>(SolowalletController);
walletService = module.get<SolowalletService>(SolowalletService);
});

it('should be defined', () => {
expect(controller).toBeDefined();
expect(walletService).toBeDefined();
});
});
22 changes: 22 additions & 0 deletions apps/api/src/solowallet/solowallet.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DepositFundsRequestDto } from '@bitsacco/common';
import { Body, Controller, Logger, Post } from '@nestjs/common';
import { ApiOperation, ApiBody } from '@nestjs/swagger';
import { SolowalletService } from './solowallet.service';

@Controller('solowallet')
export class SolowalletController {
private readonly logger = new Logger(SolowalletController.name);

constructor(private readonly walletService: SolowalletService) {
this.logger.log('SolowalletController initialized');
}

@Post('deposit')
@ApiOperation({ summary: 'Deposit funds to Solowallet' })
@ApiBody({
type: DepositFundsRequestDto,
})
depositFunds(@Body() req: DepositFundsRequestDto) {
return this.walletService.depositFunds(req);
}
}
37 changes: 37 additions & 0 deletions apps/api/src/solowallet/solowallet.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TestingModule } from '@nestjs/testing';
import { ClientGrpc } from '@nestjs/microservices';
import { SolowalletServiceClient } from '@bitsacco/common';
import { createTestingModuleWithValidation } from '@bitsacco/testing';
import { SolowalletService } from './solowallet.service';

describe('SolowalletService', () => {
let service: SolowalletService;
let serviceGenerator: ClientGrpc;
let mockSolowalletServiceClient: Partial<SolowalletServiceClient>;

beforeEach(async () => {
serviceGenerator = {
getService: jest.fn().mockReturnValue(mockSolowalletServiceClient),
getClientByServiceName: jest
.fn()
.mockReturnValue(mockSolowalletServiceClient),
};

const module: TestingModule = await createTestingModuleWithValidation({
providers: [
{
provide: SolowalletService,
useFactory: () => {
return new SolowalletService(serviceGenerator);
},
},
],
});

service = module.get<SolowalletService>(SolowalletService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
26 changes: 26 additions & 0 deletions apps/api/src/solowallet/solowallet.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
DepositFundsRequestDto,
SOLOWALLET_SERVICE_NAME,
SolowalletServiceClient,
} from '@bitsacco/common';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { type ClientGrpc } from '@nestjs/microservices';

@Injectable()
export class SolowalletService implements OnModuleInit {
private client: SolowalletServiceClient;

constructor(
@Inject(SOLOWALLET_SERVICE_NAME) private readonly grpc: ClientGrpc,
) {}

onModuleInit() {
this.client = this.grpc.getService<SolowalletServiceClient>(
SOLOWALLET_SERVICE_NAME,
);
}

depositFunds(req: DepositFundsRequestDto) {
return this.client.depositFunds(req);
}
}
3 changes: 2 additions & 1 deletion apps/solowallet/.env.manual
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
NODE_ENV='development'
SOLOWALLET_GRPC_URL='0.0.0.0:4080'
DATABASE_URL=mongodb://bs:password@mongodb:27017
SWAP_GRPC_URL='0.0.0.0:4040'
DATABASE_URL=mongodb://bs:[email protected]:27017
17 changes: 15 additions & 2 deletions apps/solowallet/src/db/solowallet.schema.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { AbstractDocument } from '@bitsacco/common';
import { AbstractDocument, TransactionStatus } from '@bitsacco/common';

@Schema({ versionKey: false })
export class SolowalletDocument extends AbstractDocument {
@Prop({ type: String, required: true })
userId: string;

@Prop({ type: Number, required: true })
quantity: number;
amountMsats: number;

@Prop({ type: Number, required: true })
amountFiat: number;

@Prop({
type: String,
enum: Object.values(TransactionStatus),
required: true,
})
status: TransactionStatus;

@Prop({ type: String, required: true })
reference: string;
}

export const SolowalletSchema =
Expand Down
2 changes: 2 additions & 0 deletions apps/solowallet/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ async function bootstrap() {

await app.startAllMicroservices();
}

bootstrap();
12 changes: 7 additions & 5 deletions apps/solowallet/src/solowallet.controller.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Controller, Get } from '@nestjs/common';
import { SolowalletService } from './solowallet.service';
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { SolowalletServiceControllerMethods } from '@bitsacco/common';
import { DepositFundsRequestDto } from 'libs/common/src/dto/solowallet.dto';
import {
SolowalletServiceControllerMethods,
DepositFundsRequestDto,
} from '@bitsacco/common';
import { SolowalletService } from './solowallet.service';

@Controller()
@SolowalletServiceControllerMethods()
export class SolowalletController {
constructor(private readonly solowalletService: SolowalletService) {}

@GrpcMethod()
depositFunds(request: DepositFundsRequestDto): string {
depositFunds(request: DepositFundsRequestDto) {
return this.solowalletService.depositFunds(request);
}
}
23 changes: 22 additions & 1 deletion apps/solowallet/src/solowallet.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import * as Joi from 'joi';
import { join } from 'path';
import { Module } from '@nestjs/common';
import { DatabaseModule, LoggerModule } from '@bitsacco/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import {
DatabaseModule,
LoggerModule,
SWAP_SERVICE_NAME,
} from '@bitsacco/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SolowalletController } from './solowallet.controller';
import { SolowalletService } from './solowallet.service';
Expand All @@ -17,6 +23,7 @@ import {
validationSchema: Joi.object({
NODE_ENV: Joi.string().required(),
SOLOWALLET_GRPC_URL: Joi.string().required(),
SWAP_GRPC_URL: Joi.string().required(),
DATABASE_URL: Joi.string().required(),
}),
}),
Expand All @@ -25,6 +32,20 @@ import {
{ name: SolowalletDocument.name, schema: SolowalletSchema },
]),
LoggerModule,
ClientsModule.registerAsync([
{
name: SWAP_SERVICE_NAME,
useFactory: (configService: ConfigService) => ({
transport: Transport.GRPC,
options: {
package: 'swap',
protoPath: join(__dirname, '../../../proto/swap.proto'),
url: configService.getOrThrow<string>('SWAP_GRPC_URL'),
},
}),
inject: [ConfigService],
},
]),
],
controllers: [SolowalletController],
providers: [SolowalletService, ConfigService, SolowalletRepository],
Expand Down
Loading

0 comments on commit fc71ef4

Please sign in to comment.