Skip to content

Commit d8d020b

Browse files
authored
feat(api): Return token details in token paths (#1778)
* feat(api): Return token details in token paths * refactor(api): lint * Add back v1 * Move v2 url into conditional * Nested objects * Add with tokens argument
1 parent 8243114 commit d8d020b

File tree

3 files changed

+115
-19
lines changed

3 files changed

+115
-19
lines changed

src/bridges/bridges.controller.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import {
1717
ChainportPort,
1818
ChainportService,
1919
ChainportToken,
20+
ChainportTokenWithNetwork,
2021
} from './chainport.service';
22+
import { TokenPathsQueryDto } from './dto/token-paths-query.dto';
2123
import { TransactionsCreateDto } from './dto/transactions-create.dto';
2224
import { TransactionsStatusDto } from './dto/transactions-status.dto';
2325
import { BridgesStatus } from './interfaces/bridge-status';
@@ -67,8 +69,18 @@ export class BridgesController {
6769
}),
6870
)
6971
token_id: number,
70-
): Promise<List<ChainportNetwork>> {
71-
const networks = await this.chainportService.getTokenPaths(token_id);
72+
@Query(
73+
new ValidationPipe({
74+
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
75+
transform: true,
76+
}),
77+
)
78+
query: TokenPathsQueryDto,
79+
): Promise<List<ChainportTokenWithNetwork | ChainportNetwork>> {
80+
const networks = await this.chainportService.getTokenPaths(
81+
token_id,
82+
query.with_tokens,
83+
);
7284

7385
return {
7486
object: 'list',

src/bridges/chainport.service.ts

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,39 @@ const chainportTokenSchema = Joi.object<ChainportToken>({
7070
is_lifi: Joi.boolean().required(),
7171
});
7272

73+
export type ChainportTokenWithNetwork = {
74+
network: ChainportNetwork;
75+
token: ChainportToken;
76+
};
77+
78+
const chainportTokenWithNetworkSchema = Joi.object<ChainportTokenWithNetwork>({
79+
network: Joi.object<ChainportNetwork>({
80+
chainport_network_id: Joi.number().positive().integer().required(),
81+
explorer_url: Joi.string().required(),
82+
label: Joi.string().required(),
83+
network_icon: Joi.string().required(),
84+
}),
85+
token: Joi.object<ChainportToken>({
86+
id: Joi.number().required(),
87+
decimals: Joi.number().required(),
88+
name: Joi.string().required(),
89+
pinned: Joi.boolean().required(),
90+
web3_address: Joi.string().required(),
91+
symbol: Joi.string().required(),
92+
token_image: Joi.string().required(),
93+
chain_id: Joi.number().allow(null).required(),
94+
network_name: Joi.string().required(),
95+
network_id: Joi.number().required(),
96+
blockchain_type: Joi.string().required(),
97+
is_stable: Joi.boolean().required(),
98+
is_lifi: Joi.boolean().required(),
99+
}),
100+
});
101+
102+
const chainportTokenWithNetworkArraySchema = Joi.array<
103+
ChainportTokenWithNetwork[]
104+
>().items(chainportTokenWithNetworkSchema);
105+
73106
const chainportTokenArraySchema =
74107
Joi.array<ChainportToken[]>().items(chainportTokenSchema);
75108

@@ -269,13 +302,17 @@ Chainport: ${token.decimals}`;
269302
return verifiedTokens;
270303
}
271304

272-
async getTokenPaths(tokenId: number): Promise<ChainportNetwork[]> {
273-
const version = this.config.get<number>('CHAINPORT_API_VERSION');
305+
async getTokenPaths(
306+
tokenId: number,
307+
withTokens = false,
308+
): Promise<ChainportNetwork[] | ChainportTokenWithNetwork[]> {
274309
const apiurl = this.config.get<string>('CHAINPORT_API_URL');
275310

276311
const metaResult = await this.getMeta();
312+
const networkList: ChainportNetwork[] = [];
313+
const networkListWithTokens: ChainportTokenWithNetwork[] = [];
277314

278-
let networkList: { label: string }[] = [];
315+
const version = this.config.get<number>('CHAINPORT_API_VERSION');
279316
if (version === 1) {
280317
const tokenListUrl = new URL(`/token/list`, apiurl);
281318
tokenListUrl.searchParams.append('network_name', 'IRONFISH');
@@ -291,41 +328,72 @@ Chainport: ${token.decimals}`;
291328
throw new NotFoundException();
292329
}
293330

294-
networkList = sourceToken.target_networks.flatMap((n) => {
331+
for (const n of sourceToken.target_networks) {
295332
const network = metaResult.cp_network_ids[n.toString()];
296333
if (!network) {
297334
this.logger.error(
298335
`Network ${n} for token ${tokenId} not found in meta`,
299336
new Error().stack ?? '',
300337
);
301-
return [];
338+
continue;
302339
}
303-
return [network];
304-
});
340+
341+
networkList.push(network);
342+
networkListWithTokens.push({
343+
network,
344+
token: {
345+
id: 0,
346+
decimals: 0,
347+
name: '',
348+
pinned: false,
349+
web3_address: '',
350+
symbol: '',
351+
token_image: '',
352+
chain_id: 0,
353+
network_name: '',
354+
network_id: 0,
355+
blockchain_type: '',
356+
is_stable: false,
357+
is_lifi: false,
358+
},
359+
});
360+
}
305361
} else {
306362
const tokenPathUrl = new URL(`/token/paths`, apiurl);
307-
308363
tokenPathUrl.searchParams.append('token_id', tokenId.toString());
309364
const tokenPathResult = await this.makeChainportRequest<ChainportToken[]>(
310365
tokenPathUrl.toString(),
311366
);
312367

313-
networkList = tokenPathResult.data.flatMap((t) => {
314-
const network = metaResult.cp_network_ids[t.network_id.toString()];
368+
for (const token of tokenPathResult.data) {
369+
const network = metaResult.cp_network_ids[token.network_id.toString()];
315370
if (!network) {
316371
this.logger.error(
317-
`Network ${t.network_id} for token ${tokenId} not found in meta`,
372+
`Network ${token.network_id} for token ${tokenId} not found in meta`,
318373
new Error().stack ?? '',
319374
);
320-
return [];
375+
continue;
321376
}
322-
return [network];
323-
});
377+
378+
networkList.push(network);
379+
networkListWithTokens.push({ token, network });
380+
}
324381
}
325382

326-
const validateResult = chainportNetworkArraySchema.validate(networkList, {
327-
stripUnknown: true,
328-
});
383+
let validateResult;
384+
385+
if (withTokens) {
386+
validateResult = chainportTokenWithNetworkArraySchema.validate(
387+
networkListWithTokens,
388+
{
389+
stripUnknown: true,
390+
},
391+
);
392+
} else {
393+
validateResult = chainportNetworkArraySchema.validate(networkList, {
394+
stripUnknown: true,
395+
});
396+
}
329397

330398
if (validateResult.error) {
331399
throw new BadGatewayException(
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
import { ApiPropertyOptional } from '@nestjs/swagger';
5+
import { Transform, TransformFnParams } from 'class-transformer';
6+
import { IsOptional } from 'class-validator';
7+
import { stringToBoolean } from '../../common/utils/boolean';
8+
9+
export class TokenPathsQueryDto {
10+
@ApiPropertyOptional({
11+
description: 'Whether or not to include tokens in the response',
12+
})
13+
@IsOptional()
14+
@Transform(({ value }: TransformFnParams) => stringToBoolean(value))
15+
readonly with_tokens?: boolean;
16+
}

0 commit comments

Comments
 (0)