diff --git a/apps/api/src/solowallet/solowallet.controller.ts b/apps/api/src/solowallet/solowallet.controller.ts index d206ec0..d00baf6 100644 --- a/apps/api/src/solowallet/solowallet.controller.ts +++ b/apps/api/src/solowallet/solowallet.controller.ts @@ -1,4 +1,7 @@ -import { DepositFundsRequestDto } from '@bitsacco/common'; +import { + DepositFundsRequestDto, + FindUserTxsRequestDto, +} from '@bitsacco/common'; import { Body, Controller, Logger, Post } from '@nestjs/common'; import { ApiOperation, ApiBody } from '@nestjs/swagger'; import { SolowalletService } from './solowallet.service'; @@ -19,4 +22,13 @@ export class SolowalletController { depositFunds(@Body() req: DepositFundsRequestDto) { return this.walletService.depositFunds(req); } + + @Post('user-deposits') + @ApiOperation({ summary: 'Find Solowallet user deposits' }) + @ApiBody({ + type: FindUserTxsRequestDto, + }) + findUserDeposits(@Body() req: FindUserTxsRequestDto) { + return this.walletService.findUserDeposits(req); + } } diff --git a/apps/api/src/solowallet/solowallet.service.ts b/apps/api/src/solowallet/solowallet.service.ts index 6d93c7e..7db612d 100644 --- a/apps/api/src/solowallet/solowallet.service.ts +++ b/apps/api/src/solowallet/solowallet.service.ts @@ -1,5 +1,6 @@ import { DepositFundsRequestDto, + FindUserTxsRequestDto, SOLOWALLET_SERVICE_NAME, SolowalletServiceClient, } from '@bitsacco/common'; @@ -23,4 +24,8 @@ export class SolowalletService implements OnModuleInit { depositFunds(req: DepositFundsRequestDto) { return this.client.depositFunds(req); } + + findUserDeposits(req: FindUserTxsRequestDto) { + return this.client.findUserDeposits(req); + } } diff --git a/apps/shares/.env.manual b/apps/shares/.env.manual index 78639d7..6a26894 100644 --- a/apps/shares/.env.manual +++ b/apps/shares/.env.manual @@ -1,4 +1,4 @@ NODE_ENV='development' SHARES_GRPC_URL='0.0.0.0:4070' SHARES_ISSUED='10000' -DATABASE_URL=mongodb://bs:password@mongodb:27017 +DATABASE_URL=mongodb://bs:password@0.0.0.0:27017 diff --git a/apps/solowallet/src/solowallet.controller.ts b/apps/solowallet/src/solowallet.controller.ts index d8bfa0d..7baacb1 100644 --- a/apps/solowallet/src/solowallet.controller.ts +++ b/apps/solowallet/src/solowallet.controller.ts @@ -3,6 +3,7 @@ import { GrpcMethod } from '@nestjs/microservices'; import { SolowalletServiceControllerMethods, DepositFundsRequestDto, + FindUserTxsRequestDto, } from '@bitsacco/common'; import { SolowalletService } from './solowallet.service'; @@ -15,4 +16,9 @@ export class SolowalletController { depositFunds(request: DepositFundsRequestDto) { return this.solowalletService.depositFunds(request); } + + @GrpcMethod() + findUserDeposits(request: FindUserTxsRequestDto) { + return this.solowalletService.findUserDeposits(request); + } } diff --git a/apps/solowallet/src/solowallet.service.ts b/apps/solowallet/src/solowallet.service.ts index b2a6aff..3d72264 100644 --- a/apps/solowallet/src/solowallet.service.ts +++ b/apps/solowallet/src/solowallet.service.ts @@ -3,7 +3,10 @@ import { CreateOnrampSwapDto, DepositFundsRequestDto, fiatToBtc, - SolowalletDepositTransaction, + FindUserTxsRequest, + PaginatedRequestDto, + PaginatedSolowalletTxsResponse, + SolowalletTxs, SWAP_SERVICE_NAME, SwapResponse, SwapServiceClient, @@ -74,7 +77,7 @@ export class SolowalletService { async depositFunds({ userId, fiatDeposit, - }: DepositFundsRequestDto): Promise { + }: DepositFundsRequestDto): Promise { const { status, reference, amountMsats, amountFiat } = fiatDeposit ? await this.initiateSwap(fiatDeposit) : { @@ -95,10 +98,38 @@ export class SolowalletService { return { ...deposit, - status, id: deposit._id, createdAt: deposit.createdAt.toDateString(), updatedAt: deposit.updatedAt.toDateString(), }; } + + async findUserDeposits({ + userId, + pagination, + }: FindUserTxsRequest): Promise { + const allDeposits = await this.wallet.find({ userId }); + + const { page, size } = pagination; + const pages = Math.ceil(allDeposits.length / size); + + // select the last page if requested page exceeds total pages possible + const selectPage = page > pages ? pages - 1 : page; + + const deposits = allDeposits + .slice(selectPage * size, (selectPage + 1) * size + size) + .map((deposit) => ({ + ...deposit, + id: deposit._id, + createdAt: deposit.createdAt.toDateString(), + updatedAt: deposit.updatedAt.toDateString(), + })); + + return { + transactions: deposits, + page: selectPage, + size, + pages, + }; + } } diff --git a/apps/swap/.env.manual b/apps/swap/.env.manual index 63500c4..4dd7da8 100644 --- a/apps/swap/.env.manual +++ b/apps/swap/.env.manual @@ -4,7 +4,7 @@ REDIS_HOST='0.0.0.0' REDIS_PORT='6379' MOCK_BTC_KES_RATE='8708520.117232416' # CURRENCY_API_KEY='foo-bar-baz' -DATABASE_URL=mongodb://bs:password@mongodb:27017 +DATABASE_URL=mongodb://bs:password@0.0.0.0:27017 INTASEND_PUBLIC_KEY=ISPubKey_test_925ab885-f06d-4ace-8507-4186413a59a4 INTASEND_PRIVATE_KEY=ISSecretKey_test_3d887e44-33c4-4455-978e-d2ae7b10907d FEDIMINT_CLIENTD_BASE_URL=http://0.0.0.0:7070 diff --git a/apps/swap/src/swap.service.ts b/apps/swap/src/swap.service.ts index 8c909bb..99f2b40 100644 --- a/apps/swap/src/swap.service.ts +++ b/apps/swap/src/swap.service.ts @@ -1,6 +1,5 @@ import { Currency, - PaginatedRequest, QuoteRequest, QuoteResponse, TransactionStatus, @@ -17,6 +16,7 @@ import { fiatToBtc, btcToFiat, SupportedCurrencyType, + PaginatedRequestDto, } from '@bitsacco/common'; import { v4 as uuidv4 } from 'uuid'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; @@ -205,7 +205,7 @@ export class SwapService { async listOnrampSwaps({ page, size, - }: PaginatedRequest): Promise { + }: PaginatedRequestDto): Promise { const onramps = await this.onramp.find({}); const pages = Math.ceil(onramps.length / size); @@ -297,7 +297,7 @@ export class SwapService { async listOfframpSwaps({ page, size, - }: PaginatedRequest): Promise { + }: PaginatedRequestDto): Promise { const offramps = await this.offramp.find({}); const pages = Math.ceil(offramps.length / size); diff --git a/libs/common/src/dto/index.ts b/libs/common/src/dto/index.ts index 47d48bd..662a7fb 100644 --- a/libs/common/src/dto/index.ts +++ b/libs/common/src/dto/index.ts @@ -1,3 +1,4 @@ +export * from './lib.dto'; export * from './swap.dto'; export * from './nostr.dto'; export * from './sms.dto'; diff --git a/libs/common/src/dto/lib.dto.ts b/libs/common/src/dto/lib.dto.ts new file mode 100644 index 0000000..185365a --- /dev/null +++ b/libs/common/src/dto/lib.dto.ts @@ -0,0 +1,16 @@ +import { Type } from 'class-transformer'; +import { IsNumber } from 'class-validator'; +import { PaginatedRequest } from '../types'; +import { ApiProperty } from '@nestjs/swagger'; + +export class PaginatedRequestDto implements PaginatedRequest { + @IsNumber() + @Type(() => Number) + @ApiProperty({ type: Number }) + page: number; + + @IsNumber() + @Type(() => Number) + @ApiProperty({ type: Number }) + size: number; +} diff --git a/libs/common/src/dto/solowallet.dto.ts b/libs/common/src/dto/solowallet.dto.ts index abb87b7..b38a130 100644 --- a/libs/common/src/dto/solowallet.dto.ts +++ b/libs/common/src/dto/solowallet.dto.ts @@ -2,7 +2,8 @@ import { Type } from 'class-transformer'; import { IsNotEmpty, IsString, ValidateNested } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { CreateOnrampSwapDto } from './swap.dto'; -import { DepositFundsRequest } from '../types'; +import { DepositFundsRequest, FindUserTxsRequest } from '../types'; +import { PaginatedRequestDto } from './lib.dto'; export class DepositFundsRequestDto implements DepositFundsRequest { @IsNotEmpty() @@ -16,3 +17,16 @@ export class DepositFundsRequestDto implements DepositFundsRequest { @ApiProperty({ type: CreateOnrampSwapDto }) fiatDeposit?: CreateOnrampSwapDto; } + +export class FindUserTxsRequestDto implements FindUserTxsRequest { + @IsNotEmpty() + @IsString() + @Type(() => String) + @ApiProperty() + userId: string; + + @ValidateNested() + @Type(() => PaginatedRequestDto) + @ApiProperty({ type: PaginatedRequestDto }) + pagination: PaginatedRequestDto; +} diff --git a/libs/common/src/types/proto/lib.ts b/libs/common/src/types/proto/lib.ts index cb2411d..3d8e1d9 100644 --- a/libs/common/src/types/proto/lib.ts +++ b/libs/common/src/types/proto/lib.ts @@ -16,3 +16,10 @@ export enum TransactionStatus { } export interface Empty {} + +export interface PaginatedRequest { + /** Page offset to start from */ + page: number; + /** Number of items to be return per page */ + size: number; +} diff --git a/libs/common/src/types/proto/solowallet.ts b/libs/common/src/types/proto/solowallet.ts index d079472..1b3e24e 100644 --- a/libs/common/src/types/proto/solowallet.ts +++ b/libs/common/src/types/proto/solowallet.ts @@ -7,7 +7,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; import { Observable } from 'rxjs'; -import { TransactionStatus } from './lib'; +import { PaginatedRequest, TransactionStatus } from './lib'; import { OnrampSwapRequest } from './swap'; export interface DepositFundsRequest { @@ -15,7 +15,7 @@ export interface DepositFundsRequest { fiatDeposit?: OnrampSwapRequest | undefined; } -export interface SolowalletDepositTransaction { +export interface SolowalletTxs { id: string; userId: string; status: TransactionStatus; @@ -26,24 +26,46 @@ export interface SolowalletDepositTransaction { updatedAt?: string | undefined; } +export interface FindUserTxsRequest { + userId: string; + pagination: PaginatedRequest | undefined; +} + +export interface PaginatedSolowalletTxsResponse { + /** List of onramp swaps */ + transactions: SolowalletTxs[]; + /** Current page offset */ + page: number; + /** Number of items return per page */ + size: number; + /** Number of pages given the current page size */ + pages: number; +} + export interface SolowalletServiceClient { - depositFunds( - request: DepositFundsRequest, - ): Observable; + depositFunds(request: DepositFundsRequest): Observable; + + findUserDeposits( + request: FindUserTxsRequest, + ): Observable; } export interface SolowalletServiceController { depositFunds( request: DepositFundsRequest, + ): Promise | Observable | SolowalletTxs; + + findUserDeposits( + request: FindUserTxsRequest, ): - | Promise - | Observable - | SolowalletDepositTransaction; + | Promise + | Observable + | PaginatedSolowalletTxsResponse; } export function SolowalletServiceControllerMethods() { return function (constructor: Function) { - const grpcMethods: string[] = ['depositFunds']; + const grpcMethods: string[] = ['depositFunds', 'findUserDeposits']; for (const method of grpcMethods) { const descriptor: any = Reflect.getOwnPropertyDescriptor( constructor.prototype, diff --git a/libs/common/src/types/proto/swap.ts b/libs/common/src/types/proto/swap.ts index 44631a4..865a4b3 100644 --- a/libs/common/src/types/proto/swap.ts +++ b/libs/common/src/types/proto/swap.ts @@ -7,7 +7,7 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices'; import { Observable } from 'rxjs'; -import { TransactionStatus } from './lib'; +import { PaginatedRequest, TransactionStatus } from './lib'; import { Bolt11 } from './lightning'; /** Currency: Enum representing supported currencies. */ @@ -135,13 +135,6 @@ export interface FindSwapRequest { id: string; } -export interface PaginatedRequest { - /** Page offset to start from */ - page: number; - /** Number of items to be return per page */ - size: number; -} - export interface PaginatedSwapResponse { /** List of onramp swaps */ swaps: SwapResponse[]; diff --git a/proto/lib.proto b/proto/lib.proto index 057a60d..d38670e 100644 --- a/proto/lib.proto +++ b/proto/lib.proto @@ -11,3 +11,10 @@ enum TransactionStatus { FAILED = 2; COMPLETE = 3; } + +message PaginatedRequest { + // Page offset to start from + int32 page = 2; + // Number of items to be return per page + int32 size = 1; +} diff --git a/proto/solowallet.proto b/proto/solowallet.proto index 9600699..cabe3ac 100644 --- a/proto/solowallet.proto +++ b/proto/solowallet.proto @@ -6,7 +6,9 @@ import "swap.proto"; package solowallet; service SolowalletService { - rpc DepositFunds(DepositFundsRequest) returns (SolowalletDepositTransaction){} + rpc DepositFunds(DepositFundsRequest) returns (SolowalletTxs){} + + rpc FindUserDeposits(FindUserTxsRequest) returns (PaginatedSolowalletTxsResponse){} } message DepositFundsRequest { @@ -17,7 +19,7 @@ message DepositFundsRequest { // Add more otional funding sources, like direct lightning deposit } -message SolowalletDepositTransaction { +message SolowalletTxs { string id = 1; string user_id = 2; @@ -36,3 +38,19 @@ message SolowalletDepositTransaction { optional string updatedAt = 12; } + +message FindUserTxsRequest { + string user_id = 1; + lib.PaginatedRequest pagination = 2; +} + +message PaginatedSolowalletTxsResponse { + // List of onramp swaps + repeated SolowalletTxs transactions = 1; + // Current page offset + int32 page = 2; + // Number of items return per page + int32 size = 3; + // Number of pages given the current page size + int32 pages = 4; +} diff --git a/proto/swap.proto b/proto/swap.proto index 17fce42..ec97aab 100644 --- a/proto/swap.proto +++ b/proto/swap.proto @@ -17,7 +17,7 @@ service SwapService { rpc FindOnrampSwap (FindSwapRequest) returns (SwapResponse) {} // ListOnrampSwaps: Lists all onramp swaps, with pagination. - rpc ListOnrampSwaps (PaginatedRequest) returns (PaginatedSwapResponse) {} + rpc ListOnrampSwaps (lib.PaginatedRequest) returns (PaginatedSwapResponse) {} // CreateOfframpSwap: Initiates an offramp swap transaction. rpc CreateOfframpSwap (OfframpSwapRequest) returns (SwapResponse) {} @@ -26,7 +26,7 @@ service SwapService { rpc FindOfframpSwap (FindSwapRequest) returns (SwapResponse) {} // ListOfframpSwaps: Lists all offramp swaps, with pagination. - rpc ListOfframpSwaps (PaginatedRequest) returns (PaginatedSwapResponse) {} + rpc ListOfframpSwaps (lib.PaginatedRequest) returns (PaginatedSwapResponse) {} } // QuoteRequest: Represents a request for a currency swap quote. @@ -153,13 +153,6 @@ enum Currency { KES = 1; } -message PaginatedRequest { - // Page offset to start from - int32 page = 2; - // Number of items to be return per page - int32 size = 1; -} - message PaginatedSwapResponse { // List of onramp swaps repeated SwapResponse swaps = 1;