Skip to content

Commit f268e17

Browse files
Merge pull request #1748 from multiversx/MEX-966-xoxno-aggregator
[MEX-966] Missing fields from smart swap pairs
2 parents 4bae5e2 + d2de3e3 commit f268e17

File tree

5 files changed

+149
-93
lines changed

5 files changed

+149
-93
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ MX_GATEWAY_URL="https://devnet-gateway.multiversx.com"
4949
MX_DEX_URL="http://localhost:3005"
5050
MX_DATA_API_URL="https://data-api.multiversx.com"
5151

52-
# XOXNO Dust Converter
52+
# XOXNO Aggregator
53+
ENABLE_XOXNO_AGGREGATOR=false
5354
XOXNO_API_URL=
5455

5556
#Enable or Disable modules

src/helpers/api.config.service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,18 @@ export class ApiConfigService {
471471
return walletPassword;
472472
}
473473

474+
isXoxnoAggregatorEnabled(): boolean {
475+
const aggregatorActive = this.configService.get<string>(
476+
'ENABLE_XOXNO_AGGREGATOR',
477+
);
478+
479+
if (!aggregatorActive) {
480+
return false;
481+
}
482+
483+
return aggregatorActive === 'true';
484+
}
485+
474486
getXoxnoApiUrl(): string | undefined {
475487
const url = this.configService.get<string>('XOXNO_API_URL');
476488
if (!url) {

src/modules/auto-router/services/auto-router.service.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
2+
import { ApiConfigService } from 'src/helpers/api.config.service';
23
import { BigNumber } from 'bignumber.js';
34
import { PairModel } from 'src/modules/pair/models/pair.model';
45
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
@@ -69,6 +70,7 @@ export class AutoRouterService {
6970
private readonly smartRouterEvaluationService: SmartRouterEvaluationService,
7071
private readonly composeTasksAbi: ComposableTasksAbiService,
7172
private readonly xoxnoAggregatorService: XoxnoAggregatorService,
73+
private readonly apiConfigService: ApiConfigService,
7274
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
7375
) {}
7476

@@ -453,14 +455,8 @@ export class AutoRouterService {
453455
const secondToken = tokenMap.get(pair.secondTokenID);
454456
return new PairModel({
455457
address: pair.address,
456-
firstToken: new EsdtToken({
457-
identifier: firstToken.identifier,
458-
decimals: firstToken.decimals,
459-
}),
460-
secondToken: new EsdtToken({
461-
identifier: secondToken.identifier,
462-
decimals: secondToken.decimals,
463-
}),
458+
firstToken,
459+
secondToken,
464460
info: allInfo[index],
465461
totalFeePercent: allTotalFeePercent[index],
466462
});
@@ -967,6 +963,10 @@ export class AutoRouterService {
967963
amountIn: string,
968964
tolerance: number,
969965
): Promise<XoxnoQuoteModel | undefined> {
966+
if (!this.apiConfigService.isXoxnoAggregatorEnabled()) {
967+
return undefined;
968+
}
969+
970970
const isEnabled =
971971
await this.remoteConfigGetterService.getXoxnoAggregatorEnabled();
972972
if (!isEnabled) {

src/modules/auto-router/specs/auto-router.service.spec.ts

Lines changed: 85 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,8 @@ describe('AutoRouterService', () => {
137137
address: Address.newFromHex(
138138
'0000000000000000000000000000000000000000000000000000000000000013',
139139
).toBech32(),
140-
firstToken: new EsdtToken({
141-
identifier: Tokens('WEGLD-123456').identifier,
142-
decimals: Tokens('WEGLD-123456').decimals,
143-
}),
144-
secondToken: new EsdtToken({
145-
identifier: Tokens('USDC-123456').identifier,
146-
decimals: Tokens('USDC-123456').decimals,
147-
}),
140+
firstToken: Tokens('WEGLD-123456'),
141+
secondToken: Tokens('USDC-123456'),
148142
info: new PairInfoModel({
149143
reserves0: '1000000000000000000000',
150144
reserves1: '10000000000',
@@ -187,14 +181,8 @@ describe('AutoRouterService', () => {
187181
address: Address.newFromHex(
188182
'0000000000000000000000000000000000000000000000000000000000000013',
189183
).toBech32(),
190-
firstToken: new EsdtToken({
191-
identifier: Tokens('WEGLD-123456').identifier,
192-
decimals: Tokens('WEGLD-123456').decimals,
193-
}),
194-
secondToken: new EsdtToken({
195-
identifier: Tokens('USDC-123456').identifier,
196-
decimals: Tokens('USDC-123456').decimals,
197-
}),
184+
firstToken: Tokens('WEGLD-123456'),
185+
secondToken: Tokens('USDC-123456'),
198186
info: new PairInfoModel({
199187
reserves0: '1000000000000000000000',
200188
reserves1: '10000000000',
@@ -242,14 +230,8 @@ describe('AutoRouterService', () => {
242230
address: Address.newFromHex(
243231
'0000000000000000000000000000000000000000000000000000000000000013',
244232
).toBech32(),
245-
firstToken: new EsdtToken({
246-
identifier: Tokens('WEGLD-123456').identifier,
247-
decimals: Tokens('WEGLD-123456').decimals,
248-
}),
249-
secondToken: new EsdtToken({
250-
identifier: Tokens('USDC-123456').identifier,
251-
decimals: Tokens('USDC-123456').decimals,
252-
}),
233+
firstToken: Tokens('WEGLD-123456'),
234+
secondToken: Tokens('USDC-123456'),
253235
info: new PairInfoModel({
254236
reserves0: '1000000000000000000000',
255237
reserves1: '10000000000',
@@ -261,14 +243,8 @@ describe('AutoRouterService', () => {
261243
address: Address.newFromHex(
262244
'0000000000000000000000000000000000000000000000000000000000000012',
263245
).toBech32(),
264-
firstToken: new EsdtToken({
265-
identifier: Tokens('WEGLD-123456').identifier,
266-
decimals: Tokens('WEGLD-123456').decimals,
267-
}),
268-
secondToken: new EsdtToken({
269-
identifier: Tokens('MEX-123456').identifier,
270-
decimals: Tokens('MEX-123456').decimals,
271-
}),
246+
firstToken: Tokens('WEGLD-123456'),
247+
secondToken: Tokens('MEX-123456'),
272248
info: new PairInfoModel({
273249
reserves0: '1000000000000000000000',
274250
reserves1: '1000000000000000000000000',
@@ -557,21 +533,39 @@ describe('AutoRouterService', () => {
557533
describe('XOXNO Aggregator Integration', () => {
558534
let xoxnoService: any;
559535
let remoteConfigService: any;
536+
let apiConfigService: any;
560537

561538
beforeEach(() => {
562539
jest.clearAllMocks();
563540
xoxnoService = service['xoxnoAggregatorService'];
564541
remoteConfigService = service['remoteConfigGetterService'];
565-
566-
jest.spyOn(remoteConfigService, 'getSmartSwapFlagValue').mockResolvedValue(true);
567-
jest.spyOn(remoteConfigService, 'getMinSmartSwapDeltaPercentage').mockResolvedValue(new BigNumber(0));
568-
jest.spyOn(remoteConfigService, 'getXoxnoAggregatorEnabled').mockResolvedValue(true);
542+
apiConfigService = service['apiConfigService'];
543+
544+
jest.spyOn(
545+
remoteConfigService,
546+
'getSmartSwapFlagValue',
547+
).mockResolvedValue(true);
548+
jest.spyOn(
549+
remoteConfigService,
550+
'getMinSmartSwapDeltaPercentage',
551+
).mockResolvedValue(new BigNumber(0));
552+
jest.spyOn(
553+
remoteConfigService,
554+
'getXoxnoAggregatorEnabled',
555+
).mockResolvedValue(true);
556+
jest.spyOn(
557+
apiConfigService,
558+
'isXoxnoAggregatorEnabled',
559+
).mockResolvedValue(true);
569560
});
570561

571562
it('should set smartSwap.source = XOXNO when XOXNO provides best output', async () => {
572563
// Mock internal smart swap to be worse than XOXNO
573564
jest.spyOn(service as any, 'computeSmartSwap').mockResolvedValue(
574-
new AutoRouteModel({ amountOut: '200000000000000000', source: 'internal' } as any)
565+
new AutoRouteModel({
566+
amountOut: '200000000000000000',
567+
source: 'internal',
568+
} as any),
575569
);
576570
jest.spyOn(xoxnoService, 'getQuote').mockResolvedValue({
577571
from: 'USDC-123456',
@@ -582,18 +576,22 @@ describe('AutoRouterService', () => {
582576
slippage: 0.01,
583577
priceImpact: 1.5,
584578
rate: 150000000,
585-
paths: [{
586-
amountIn: '2000000',
587-
amountOut: '300000000000000000',
588-
swaps: [{
589-
dex: 'XExchange',
590-
address: 'erd1qqq',
591-
from: 'USDC-123456',
592-
to: 'WEGLD-123456',
579+
paths: [
580+
{
593581
amountIn: '2000000',
594582
amountOut: '300000000000000000',
595-
}],
596-
}],
583+
swaps: [
584+
{
585+
dex: 'XExchange',
586+
address: 'erd1qqq',
587+
from: 'USDC-123456',
588+
to: 'WEGLD-123456',
589+
amountIn: '2000000',
590+
amountOut: '300000000000000000',
591+
},
592+
],
593+
},
594+
],
597595
}); // XOXNO wins
598596

599597
const swap = await service.swap({
@@ -612,7 +610,10 @@ describe('AutoRouterService', () => {
612610

613611
it('should set smartSwap.source = INTERNAL when internal smart-swap is better', async () => {
614612
jest.spyOn(service as any, 'computeSmartSwap').mockResolvedValue(
615-
new AutoRouteModel({ amountOut: '300000000000000000', source: 'internal' } as any)
613+
new AutoRouteModel({
614+
amountOut: '300000000000000000',
615+
source: 'internal',
616+
} as any),
616617
);
617618
jest.spyOn(xoxnoService, 'getQuote').mockResolvedValue({
618619
from: 'USDC-123456',
@@ -638,7 +639,10 @@ describe('AutoRouterService', () => {
638639
});
639640

640641
it('should skip XOXNO when feature flag is disabled', async () => {
641-
jest.spyOn(remoteConfigService, 'getXoxnoAggregatorEnabled').mockResolvedValue(false);
642+
jest.spyOn(
643+
remoteConfigService,
644+
'getXoxnoAggregatorEnabled',
645+
).mockResolvedValue(false);
642646
jest.spyOn(xoxnoService, 'getQuote');
643647

644648
const swap = await service.swap({
@@ -669,9 +673,15 @@ describe('AutoRouterService', () => {
669673

670674
it('should return XOXNO transaction when source is XOXNO', async () => {
671675
const mockTx = {
672-
receiver: 'erd1xoxno', data: 'swap', value: '0',
673-
sender: senderAddress, gasLimit: 100000, gasPrice: 1000000000,
674-
chainId: '1', version: 1, nonce: 0
676+
receiver: 'erd1xoxno',
677+
data: 'swap',
678+
value: '0',
679+
sender: senderAddress,
680+
gasLimit: 100000,
681+
gasPrice: 1000000000,
682+
chainId: '1',
683+
version: 1,
684+
nonce: 0,
675685
};
676686

677687
const parentModel = new AutoRouteModel({
@@ -681,39 +691,51 @@ describe('AutoRouterService', () => {
681691
tolerance: 0.01,
682692
smartSwap: {
683693
source: 'xoxno',
684-
amountOut: '300000000000000000'
694+
amountOut: '300000000000000000',
685695
} as any,
686-
transactions: [
687-
new TransactionModel(mockTx)
688-
]
696+
transactions: [new TransactionModel(mockTx)],
689697
});
690698

691-
const txs = await service.getTransactions(senderAddress, parentModel);
699+
const txs = await service.getTransactions(
700+
senderAddress,
701+
parentModel,
702+
);
692703

693704
expect(txs).toBeDefined();
694705
expect(txs[0]).toEqual(mockTx);
695706
});
696707

697708
it('should fall back to internal smart-swap when XOXNO tx fetch fails', async () => {
698-
jest.spyOn(xoxnoService, 'getQuote').mockRejectedValue(new Error('API failed'));
709+
jest.spyOn(xoxnoService, 'getQuote').mockRejectedValue(
710+
new Error('API failed'),
711+
);
699712

700713
// Should fallback to internal tx generation which calls ComposableTasksTransactionService
701714
const composableTxService = service['autoRouterTransactionService'];
702-
jest.spyOn(composableTxService, 'smartSwap').mockResolvedValue([{ receiver: 'erd1internal' } as any]);
715+
jest.spyOn(composableTxService, 'smartSwap').mockResolvedValue([
716+
{ receiver: 'erd1internal' } as any,
717+
]);
703718

704719
const parentModel = new AutoRouteModel({
705720
tokenInID: 'WEGLD-123456',
706721
tokenOutID: 'USDC-123456',
707722
amountIn: '2000000',
708723
tolerance: 0.01,
709-
parallelRouteSwap: { allocations: [{ addressRoute: [], intermediaryAmounts: [] }] } as any,
724+
parallelRouteSwap: {
725+
allocations: [
726+
{ addressRoute: [], intermediaryAmounts: [] },
727+
],
728+
} as any,
710729
smartSwap: {
711730
source: 'internal',
712-
amountOut: '300000000000000000'
713-
} as any
731+
amountOut: '300000000000000000',
732+
} as any,
714733
});
715734

716-
const txs = await service.getTransactions(senderAddress, parentModel);
735+
const txs = await service.getTransactions(
736+
senderAddress,
737+
parentModel,
738+
);
717739

718740
expect(txs).toBeDefined();
719741
expect(txs[0].receiver).toBe('erd1internal'); // Fell back successfully

0 commit comments

Comments
 (0)