Skip to content

Commit f82214a

Browse files
committed
feat(renegade): rebuild integration for v2 rfq flow
1 parent 13b1b3f commit f82214a

File tree

9 files changed

+731
-450
lines changed

9 files changed

+731
-450
lines changed

src/dex/renegade/api/renegade-client.ts

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
// RenegadeClient - Client for Renegade match endpoint API
2-
// Handles authentication and HTTP requests to Renegade's external match endpoint.
3-
41
import { Network } from '../../../constants';
52
import { IDexHelper } from '../../../dex-helper/idex-helper';
63
import { Logger } from '../../../types';
74
import { generateRenegadeAuthHeaders } from './auth';
85
import {
9-
buildRenegadeApiUrl,
6+
buildRenegadeV2ApiUrl,
107
RENEGADE_API_TIMEOUT_MS,
11-
RENEGADE_QUOTE_ENDPOINT,
128
RENEGADE_ASSEMBLE_ENDPOINT,
9+
RENEGADE_QUOTE_ENDPOINT,
1310
} from '../constants';
1411
import {
1512
AssembleExternalMatchRequest,
@@ -21,13 +18,11 @@ import {
2118
SponsoredQuoteResponse,
2219
} from './types';
2320

24-
// Default headers for Renegade API requests
2521
const DEFAULT_RENEGADE_HEADERS: Record<string, string> = {
2622
'content-type': 'application/json',
27-
accept: 'application/json;number=string',
23+
accept: 'application/json',
2824
};
2925

30-
// Client for interacting with Renegade match endpoint API
3126
export class RenegadeClient {
3227
private readonly baseUrl: string;
3328

@@ -38,10 +33,9 @@ export class RenegadeClient {
3833
private readonly apiSecret: string,
3934
private readonly logger: Logger,
4035
) {
41-
this.baseUrl = buildRenegadeApiUrl(this.network);
36+
this.baseUrl = buildRenegadeV2ApiUrl(this.network);
4237
}
4338

44-
// Request a quote from Renegade API
4539
async requestQuote(
4640
externalOrder: ExternalOrder,
4741
queryParams?: QuoteQueryParams,
@@ -65,40 +59,50 @@ export class RenegadeClient {
6559

6660
this.logger.debug('Requesting quote from Renegade API', {
6761
url: quoteUrl,
68-
order: externalOrder,
62+
externalOrder,
6963
queryParams,
7064
});
7165

72-
const response: SponsoredQuoteResponse =
73-
await this.dexHelper.httpRequest.post(
66+
try {
67+
return await this.dexHelper.httpRequest.post(
7468
quoteUrl,
7569
requestBody,
7670
RENEGADE_API_TIMEOUT_MS,
7771
headers,
7872
);
79-
80-
return response;
73+
} catch (e: any) {
74+
this.logger.error('Renegade quote request failed', {
75+
url: quoteUrl,
76+
requestBody,
77+
status: e?.response?.status,
78+
responseData: e?.response?.data,
79+
});
80+
throw e;
81+
}
8182
}
8283

83-
// Assemble an external match from a signed quote
8484
async assembleExternalMatch(
8585
signedQuote: SignedExternalQuote,
86-
queryParams?: QuoteQueryParams & {
87-
updated_order?: ExternalOrder | null;
86+
options?: {
87+
updatedOrder?: ExternalOrder | null;
88+
receiverAddress?: string | null;
89+
queryParams?: QuoteQueryParams;
90+
doGasEstimation?: boolean;
8891
},
8992
): Promise<SponsoredMatchResponse> {
9093
const requestBody: AssembleExternalMatchRequest = {
91-
signed_quote: signedQuote,
94+
do_gas_estimation: options?.doGasEstimation ?? false,
95+
receiver_address: options?.receiverAddress,
96+
order: {
97+
type: 'quoted-order',
98+
signed_quote: signedQuote,
99+
updated_order: options?.updatedOrder,
100+
},
92101
};
93102

94-
if (queryParams?.updated_order !== undefined) {
95-
requestBody.updated_order = queryParams.updated_order;
96-
}
97-
98-
const { updated_order, ...urlQueryParams } = queryParams || {};
99103
const { url: assembleUrl, pathWithQuery } = this.buildUrlWithQueryParams(
100104
RENEGADE_ASSEMBLE_ENDPOINT,
101-
urlQueryParams,
105+
options?.queryParams,
102106
);
103107

104108
const headers = generateRenegadeAuthHeaders(
@@ -111,25 +115,27 @@ export class RenegadeClient {
111115

112116
this.logger.debug('Assembling external match from Renegade API', {
113117
url: assembleUrl,
114-
queryParams,
118+
options,
115119
});
116120

117-
const response: SponsoredMatchResponse =
118-
await this.dexHelper.httpRequest.post(
121+
try {
122+
return await this.dexHelper.httpRequest.post(
119123
assembleUrl,
120124
requestBody,
121125
RENEGADE_API_TIMEOUT_MS,
122126
headers,
123127
);
124-
125-
this.logger.debug(
126-
'Assembled external match from Renegade API',
127-
JSON.stringify(response, null, 2),
128-
);
129-
130-
return response;
128+
} catch (e: any) {
129+
this.logger.error('Renegade assemble request failed', {
130+
url: assembleUrl,
131+
requestBody,
132+
status: e?.response?.status,
133+
responseData: e?.response?.data,
134+
});
135+
throw e;
136+
}
131137
}
132-
// Build URL with query parameters from endpoint path and optional query params
138+
133139
private buildUrlWithQueryParams(
134140
endpoint: string,
135141
queryParams?: QuoteQueryParams,
@@ -144,6 +150,7 @@ export class RenegadeClient {
144150
}
145151
url.search = searchParams.toString();
146152
}
153+
147154
return {
148155
url: url.toString(),
149156
pathWithQuery: url.pathname + url.search,

src/dex/renegade/api/types.ts

Lines changed: 53 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,82 @@
1-
// Request body for assemble external match endpoint
2-
export type AssembleExternalMatchRequest = {
3-
signed_quote: SignedExternalQuote;
4-
do_gas_estimation?: boolean;
5-
allow_shared?: boolean;
6-
matching_pool?: string;
7-
relayer_fee_rate?: number;
8-
receiver_address?: string | null;
9-
updated_order?: ExternalOrder | null;
1+
export type QuoteQueryParams = {
2+
disable_gas_sponsorship?: boolean;
3+
refund_address?: string;
4+
refund_native_eth?: boolean;
105
};
116

12-
// External order structure for Renegade match requests
137
export type ExternalOrder = {
14-
quote_mint: string;
15-
base_mint: string;
16-
side: 'Buy' | 'Sell';
17-
base_amount: string;
18-
quote_amount: string;
19-
exact_base_output: string;
20-
exact_quote_output: string;
8+
input_mint: string;
9+
output_mint: string;
10+
input_amount: string;
11+
output_amount: string;
12+
use_exact_output_amount: boolean;
2113
min_fill_size: string;
2214
};
2315

24-
// Request body for quote endpoint
2516
export type ExternalQuoteRequest = {
2617
external_order: ExternalOrder;
27-
matching_pool?: string;
28-
relayer_fee_rate?: number;
29-
};
30-
31-
// Quote with signature binding it to the relayer
32-
export type SignedExternalQuote = {
33-
quote: ApiExternalQuote;
34-
signature: string;
35-
};
36-
37-
// Response from assemble endpoint
38-
export type SponsoredMatchResponse = {
39-
match_bundle: AtomicMatchApiBundle;
40-
is_sponsored: boolean;
41-
gas_sponsorship_info?: GasSponsorshipInfo | null;
42-
};
43-
44-
// Response from quote endpoint with signed quote and optional sponsorship
45-
export type SponsoredQuoteResponse = {
46-
signed_quote: SignedExternalQuote;
47-
gas_sponsorship_info?: SignedGasSponsorshipInfo | null;
4818
};
4919

50-
// Optional query parameters for quote and assembly endpoints
51-
export type QuoteQueryParams = {
52-
disable_gas_sponsorship?: boolean;
53-
refund_address?: string;
54-
refund_native_eth?: boolean;
55-
use_gas_sponsorship?: boolean;
56-
};
57-
58-
// Represents a token transfer with mint address and amount
59-
type ApiExternalAssetTransfer = {
20+
type AssetTransfer = {
6021
mint: string;
6122
amount: string;
6223
};
6324

64-
// Match result with quote/base mints, amounts, and direction
65-
type ApiExternalMatchResult = {
66-
quote_mint: string;
67-
base_mint: string;
68-
quote_amount: string;
69-
base_amount: string;
70-
direction: 'Buy' | 'Sell';
25+
type ExternalQuote = {
26+
order: ExternalOrder;
27+
send: AssetTransfer;
28+
receive: AssetTransfer;
7129
};
7230

73-
// Price with timestamp
74-
type ApiTimestampedPrice = {
75-
price: string;
76-
timestamp: number;
31+
export type SignedExternalQuote = {
32+
quote: ExternalQuote;
33+
signature: string;
34+
deadline: number | string;
7735
};
7836

79-
// Complete match bundle with settlement transaction
80-
type AtomicMatchApiBundle = {
81-
match_result: ApiExternalMatchResult;
82-
fees: FeeTake;
83-
receive: ApiExternalAssetTransfer;
84-
send: ApiExternalAssetTransfer;
85-
settlement_tx: TransactionRequest;
37+
export type SponsoredQuoteResponse = {
38+
signed_quote: SignedExternalQuote;
39+
gas_sponsorship_info?: {
40+
refund_amount: string;
41+
refund_native_eth: boolean;
42+
refund_address: string | null;
43+
} | null;
8644
};
8745

88-
// Complete quote structure with order, match result, fees, and pricing
89-
type ApiExternalQuote = {
90-
order: ExternalOrder;
91-
match_result: ApiExternalMatchResult;
92-
fees: FeeTake;
93-
send: ApiExternalAssetTransfer;
94-
receive: ApiExternalAssetTransfer;
95-
price: ApiTimestampedPrice;
96-
timestamp: number;
46+
type QuotedOrderAssembly = {
47+
type: 'quoted-order';
48+
signed_quote: SignedExternalQuote;
49+
updated_order?: ExternalOrder | null;
9750
};
9851

99-
// Relayer and protocol fees
100-
type FeeTake = {
101-
relayer_fee: string;
102-
protocol_fee: string;
52+
type DirectOrderAssembly = {
53+
type: 'direct-order';
54+
external_order: ExternalOrder;
10355
};
10456

105-
// Gas refund details
106-
type GasSponsorshipInfo = {
107-
refund_amount: string;
108-
refund_native_eth: boolean;
109-
refund_address: string | null;
57+
export type AssembleExternalMatchRequest = {
58+
do_gas_estimation?: boolean;
59+
receiver_address?: string | null;
60+
order: QuotedOrderAssembly | DirectOrderAssembly;
11061
};
11162

112-
// Signed gas sponsorship info (deprecated signature field)
113-
type SignedGasSponsorshipInfo = {
114-
gas_sponsorship_info: GasSponsorshipInfo;
115-
signature: string; // deprecated
63+
export type SponsoredMatchResponse = {
64+
match_bundle: {
65+
min_receive: AssetTransfer;
66+
max_receive: AssetTransfer;
67+
min_send: AssetTransfer;
68+
max_send: AssetTransfer;
69+
deadline: number | string;
70+
settlement_tx: TransactionRequest;
71+
};
72+
input_amount?: string | null;
73+
gas_sponsorship_info?: {
74+
refund_amount: string;
75+
refund_native_eth: boolean;
76+
refund_address: string | null;
77+
} | null;
11678
};
11779

118-
// EIP-1559 / EIP-4844 aware transaction request
11980
type TransactionRequest = {
12081
from?: string | null;
12182
to?: string | null;
@@ -135,6 +96,6 @@ type TransactionRequest = {
13596
}>;
13697
type?: string;
13798
blobVersionedHashes?: string[];
138-
sidecar?: Record<string, any>;
139-
authorizationList?: Array<Record<string, any>>;
99+
sidecar?: Record<string, unknown>;
100+
authorizationList?: Array<Record<string, unknown>>;
140101
};

src/dex/renegade/constants.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,40 @@ import { Network } from '../../constants';
33

44
export const RENEGADE_NAME = 'Renegade';
55

6-
// Base API URLs for each network
7-
export const RENEGADE_ARBITRUM_BASE_URL =
6+
// Legacy RFQT host (used for levels polling only).
7+
export const RENEGADE_ARBITRUM_RFQT_BASE_URL =
88
'https://arbitrum-one.auth-server.renegade.fi';
9-
export const RENEGADE_BASE_BASE_URL =
9+
export const RENEGADE_BASE_RFQT_BASE_URL =
1010
'https://base-mainnet.auth-server.renegade.fi';
1111

12+
// V2 host (used for external match quote/assemble).
13+
export const RENEGADE_ARBITRUM_V2_BASE_URL =
14+
'https://arbitrum-one.v2.auth-server.renegade.fi';
15+
export const RENEGADE_BASE_V2_BASE_URL =
16+
'https://base-mainnet.v2.auth-server.renegade.fi';
17+
1218
export const RENEGADE_LEVELS_ENDPOINT = '/rfqt/v3/levels';
13-
export const RENEGADE_QUOTE_ENDPOINT = '/v0/matching-engine/quote';
19+
export const RENEGADE_QUOTE_ENDPOINT = '/v2/external-matches/get-quote';
1420
export const RENEGADE_ASSEMBLE_ENDPOINT =
15-
'/v0/matching-engine/assemble-external-match';
21+
'/v2/external-matches/assemble-match-bundle';
22+
23+
export function buildRenegadeRfqtApiUrl(network: Network): string {
24+
switch (network) {
25+
case Network.ARBITRUM:
26+
return RENEGADE_ARBITRUM_RFQT_BASE_URL;
27+
case Network.BASE:
28+
return RENEGADE_BASE_RFQT_BASE_URL;
29+
default:
30+
throw new Error(`Network ${network} is not supported by Renegade`);
31+
}
32+
}
1633

17-
// Get Renegade API base URL for a specific network.
18-
export function buildRenegadeApiUrl(network: Network): string {
34+
export function buildRenegadeV2ApiUrl(network: Network): string {
1935
switch (network) {
2036
case Network.ARBITRUM:
21-
return RENEGADE_ARBITRUM_BASE_URL;
37+
return RENEGADE_ARBITRUM_V2_BASE_URL;
2238
case Network.BASE:
23-
return RENEGADE_BASE_BASE_URL;
39+
return RENEGADE_BASE_V2_BASE_URL;
2440
default:
2541
throw new Error(`Network ${network} is not supported by Renegade`);
2642
}
@@ -35,9 +51,6 @@ export const RENEGADE_TOKEN_METADATA_CACHE_TTL_SECONDS = 24 * 60 * 60; // 24 hou
3551
export const RENEGADE_TOKEN_METADATA_POLLING_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
3652
export const RENEGADE_TOKEN_METADATA_CACHE_KEY = 'renegade_token_metadata';
3753

38-
export const RENEGADE_QUOTE_CACHE_TTL_SECONDS = 5;
39-
export const RENEGADE_QUOTE_CACHE_KEY = 'renegade_quote';
40-
4154
// API timeout settings
4255
export const RENEGADE_API_TIMEOUT_MS = 10_000;
4356
export const RENEGADE_INIT_TIMEOUT_MS = 5_000;

0 commit comments

Comments
 (0)