Skip to content

Commit

Permalink
feat: make IQuoteRequest interface
Browse files Browse the repository at this point in the history
Have HardQuoteRequest implement interface instead of squanch into
a QuoteRequest. should make it easier to vary the logic for each in the
future
  • Loading branch information
marktoda committed Apr 17, 2024
1 parent 342e9bf commit d0406b9
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 43 deletions.
57 changes: 44 additions & 13 deletions lib/entities/HardQuoteRequest.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { TradeType } from '@uniswap/sdk-core';
import { UnsignedV2DutchOrder } from '@uniswap/uniswapx-sdk';
import { UnsignedV2DutchOrder, V2DutchOrderBuilder } from '@uniswap/uniswapx-sdk';
import { BigNumber, ethers, utils } from 'ethers';

import { QuoteRequest, QuoteRequestDataJSON } from '.';
import { HardQuoteRequestBody } from '../handlers/hard-quote';
import { ExternalRFQDataJSON, IQuoteRequest } from '.';

export class HardQuoteRequest {
export class HardQuoteRequest implements IQuoteRequest {
public order: UnsignedV2DutchOrder;

public static fromHardRequestBody(body: HardQuoteRequestBody): HardQuoteRequest {
Expand All @@ -16,12 +16,12 @@ export class HardQuoteRequest {
this.order = UnsignedV2DutchOrder.parse(data.encodedInnerOrder, data.tokenInChainId);
}

public toCleanJSON(): QuoteRequestDataJSON {
public toJSON(): ExternalRFQDataJSON {
return {
tokenInChainId: this.tokenInChainId,
tokenOutChainId: this.tokenOutChainId,
swapper: ethers.constants.AddressZero,
requestId: this.requestId,
swapper: this.swapper,
tokenIn: this.tokenIn,
tokenOut: this.tokenOut,
amount: this.amount.toString(),
Expand All @@ -31,9 +31,16 @@ export class HardQuoteRequest {
};
}

public toCleanJSON(): ExternalRFQDataJSON {
return {
...this.toJSON(),
swapper: ethers.constants.AddressZero,
};
}

// return an opposing quote request,
// i.e. quoting the other side of the trade
public toOpposingCleanJSON(): QuoteRequestDataJSON {
public toOpposingCleanJSON(): ExternalRFQDataJSON {
const type = this.type === TradeType.EXACT_INPUT ? TradeType.EXACT_OUTPUT : TradeType.EXACT_INPUT;
return {
...this.toCleanJSON(),
Expand All @@ -46,13 +53,24 @@ export class HardQuoteRequest {
};
}

// transforms into a quote request that can be used to query quoters
public toQuoteRequest(): QuoteRequest {
return new QuoteRequest({
...this.toCleanJSON(),
swapper: this.swapper,
amount: this.amount,
type: this.type,
public toOpposingRequest(): IQuoteRequest {
const oppositeOrder = V2DutchOrderBuilder.fromOrder(this.order)
.input({
startAmount: this.totalOutputAmountStart,
endAmount: this.totalOutputAmountEnd,
token: this.tokenOut,
})
.output({
startAmount: this.totalInputAmountStart,
endAmount: this.totalInputAmountEnd,
token: this.tokenIn,
recipient: this.swapper,
})
.buildPartial();

return new HardQuoteRequest({
...this.data,
encodedInnerOrder: oppositeOrder.serialize(),
});
}

Expand Down Expand Up @@ -89,10 +107,23 @@ export class HardQuoteRequest {
return amount;
}

public get totalOutputAmountEnd(): BigNumber {
let amount = BigNumber.from(0);
for (const output of this.order.info.outputs) {
amount = amount.add(output.endAmount);
}

return amount;
}

public get totalInputAmountStart(): BigNumber {
return this.order.info.input.startAmount;
}

public get totalInputAmountEnd(): BigNumber {
return this.order.info.input.endAmount;
}

public get amount(): BigNumber {
if (this.type === TradeType.EXACT_INPUT) {
return this.totalInputAmountStart;
Expand Down
20 changes: 14 additions & 6 deletions lib/entities/QuoteRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@ export interface QuoteRequestData {
quoteId?: string;
}

export interface QuoteRequestDataJSON extends Omit<QuoteRequestData, 'amount' | 'type'> {
// the data sent to external RFQ providers
export interface ExternalRFQDataJSON extends Omit<QuoteRequestData, 'amount' | 'type'> {
amount: string;
type: string;
}

export interface IQuoteRequest extends QuoteRequestData {
toJSON(): ExternalRFQDataJSON;
toCleanJSON(): ExternalRFQDataJSON;
toOpposingCleanJSON(): ExternalRFQDataJSON;
toOpposingRequest(): IQuoteRequest;
}

// data class for QuoteRequest helpers and conversions
export class QuoteRequest {
export class QuoteRequest implements IQuoteRequest {
public static fromRequestBody(body: PostQuoteRequestBody): QuoteRequest {
return new QuoteRequest({
tokenInChainId: body.tokenInChainId,
Expand All @@ -40,7 +48,7 @@ export class QuoteRequest {

constructor(private data: QuoteRequestData) {}

public toJSON(): QuoteRequestDataJSON {
public toJSON(): ExternalRFQDataJSON {
return {
tokenInChainId: this.tokenInChainId,
tokenOutChainId: this.tokenOutChainId,
Expand All @@ -55,7 +63,7 @@ export class QuoteRequest {
};
}

public toCleanJSON(): QuoteRequestDataJSON {
public toCleanJSON(): ExternalRFQDataJSON {
return {
tokenInChainId: this.tokenInChainId,
tokenOutChainId: this.tokenOutChainId,
Expand All @@ -72,7 +80,7 @@ export class QuoteRequest {

// return an opposing quote request,
// i.e. quoting the other side of the trade
public toOpposingCleanJSON(): QuoteRequestDataJSON {
public toOpposingCleanJSON(): ExternalRFQDataJSON {
const type = this.type === TradeType.EXACT_INPUT ? TradeType.EXACT_OUTPUT : TradeType.EXACT_INPUT;
return {
tokenInChainId: this.tokenOutChainId,
Expand All @@ -90,7 +98,7 @@ export class QuoteRequest {
};
}

public toOpposingRequest(): QuoteRequest {
public toOpposingRequest(): IQuoteRequest {
const opposingJSON = this.toOpposingCleanJSON();
return new QuoteRequest({
...opposingJSON,
Expand Down
9 changes: 1 addition & 8 deletions lib/handlers/hard-quote/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,7 @@ export class QuoteHandler extends APIGLambdaHandler<
},
});

const bestQuote = await getBestQuote(
quoters,
request.toQuoteRequest(),
log,
metric,
ProtocolVersion.V2,
'HardResponse'
);
const bestQuote = await getBestQuote(quoters, request, log, metric, ProtocolVersion.V2, 'HardResponse');
if (!bestQuote && !requestBody.allowNoQuote) {
if (!requestBody.allowNoQuote) {
throw new NoQuotesAvailable();
Expand Down
2 changes: 1 addition & 1 deletion lib/handlers/hard-quote/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { QuoteHandler as HardQuoteHandler } from './handler';
export { RequestInjected, ContainerInjected, QuoteInjector as HardQuoteInjector } from './injector';
export { ContainerInjected, QuoteInjector as HardQuoteInjector, RequestInjected } from './injector';
export * from './schema';
4 changes: 2 additions & 2 deletions lib/handlers/quote/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IMetric, MetricLoggerUnit } from '@uniswap/smart-order-router';
import Logger from 'bunyan';
import Joi from 'joi';

import { Metric, QuoteRequest, QuoteResponse } from '../../entities';
import { IQuoteRequest, Metric, QuoteRequest, QuoteResponse } from '../../entities';
import { ProtocolVersion } from '../../providers';
import { Quoter } from '../../quoters';
import { NoQuotesAvailable } from '../../util/errors';
Expand Down Expand Up @@ -84,7 +84,7 @@ export class QuoteHandler extends APIGLambdaHandler<
// fetch quotes from all quoters and return the best one
export async function getBestQuote(
quoters: Quoter[],
quoteRequest: QuoteRequest,
quoteRequest: IQuoteRequest,
log: Logger,
metric: IMetric,
protocolVersion: ProtocolVersion = ProtocolVersion.V1,
Expand Down
2 changes: 1 addition & 1 deletion lib/handlers/quote/injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
FADE_RATE_BUCKET,
FADE_RATE_S3_KEY,
INTEGRATION_S3_KEY,
PRODUCTION_S3_KEY,
PROD_COMPLIANCE_S3_KEY,
PRODUCTION_S3_KEY,
WEBHOOK_CONFIG_BUCKET,
} from '../../constants';
import {
Expand Down
2 changes: 1 addition & 1 deletion lib/providers/circuit-breaker/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { NodeHttpHandler } from '@smithy/node-http-handler';
import { MetricsLogger, Unit } from 'aws-embedded-metrics';
import Logger from 'bunyan';

import { CircuitBreakerConfiguration, CircuitBreakerConfigurationProvider } from '.';
import { Metric } from '../../entities';
import { checkDefined } from '../../preconditions/preconditions';
import { CircuitBreakerConfiguration, CircuitBreakerConfigurationProvider } from '.';

export class S3CircuitBreakerConfigurationProvider implements CircuitBreakerConfigurationProvider {
private log: Logger;
Expand Down
2 changes: 1 addition & 1 deletion lib/providers/compliance/s3.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { default as Logger } from 'bunyan';

import { FillerComplianceConfiguration, FillerComplianceConfigurationProvider } from '.';
import { checkDefined } from '../../preconditions/preconditions';
import { FillerComplianceConfiguration, FillerComplianceConfigurationProvider } from '.';

export class S3FillerComplianceConfigurationProvider implements FillerComplianceConfigurationProvider {
private log: Logger;
Expand Down
2 changes: 1 addition & 1 deletion lib/providers/webhook/s3.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { default as Logger } from 'bunyan';

import { ProtocolVersion, WebhookConfiguration, WebhookConfigurationProvider } from '.';
import { checkDefined } from '../../preconditions/preconditions';
import { ProtocolVersion, WebhookConfiguration, WebhookConfigurationProvider } from '.';

export type FillerAddressesMap = Map<string, Set<string>>;

Expand Down
2 changes: 1 addition & 1 deletion lib/quoters/MockQuoter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Logger from 'bunyan';
import { BigNumber } from 'ethers';

import { Quoter, QuoterType } from '.';
import { QuoteRequest, QuoteResponse } from '../entities';
import { Quoter, QuoterType } from '.';

export const MOCK_FILLER_ADDRESS = '0x0000000000000000000000000000000000000001';

Expand Down
10 changes: 5 additions & 5 deletions lib/quoters/WebhookQuoter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import axios, { AxiosError, AxiosResponse } from 'axios';
import Logger from 'bunyan';
import { v4 as uuidv4 } from 'uuid';

import { Quoter, QuoterType } from '.';
import {
AnalyticsEvent,
AnalyticsEventType,
IQuoteRequest,
Metric,
metricContext,
QuoteRequest,
QuoteResponse,
WebhookResponseType,
} from '../entities';
Expand All @@ -19,6 +18,7 @@ import { FirehoseLogger } from '../providers/analytics';
import { CircuitBreakerConfigurationProvider } from '../providers/circuit-breaker';
import { FillerComplianceConfigurationProvider } from '../providers/compliance';
import { timestampInMstoISOString } from '../util/time';
import { Quoter, QuoterType } from '.';

// TODO: shorten, maybe take from env config
const WEBHOOK_TIMEOUT_MS = 500;
Expand All @@ -41,7 +41,7 @@ export class WebhookQuoter implements Quoter {
this.ALLOW_LIST = _allow_list;
}

public async quote(request: QuoteRequest, version: ProtocolVersion = ProtocolVersion.V1): Promise<QuoteResponse[]> {
public async quote(request: IQuoteRequest, version: ProtocolVersion = ProtocolVersion.V1): Promise<QuoteResponse[]> {
let endpoints = await this.getEligibleEndpoints();
const endpointToAddrsMap = await this.complianceProvider.getEndpointToExcludedAddrsMap();
endpoints = endpoints.filter(
Expand Down Expand Up @@ -90,7 +90,7 @@ export class WebhookQuoter implements Quoter {
}
}

private async fetchQuote(config: WebhookConfiguration, request: QuoteRequest): Promise<QuoteResponse | null> {
private async fetchQuote(config: WebhookConfiguration, request: IQuoteRequest): Promise<QuoteResponse | null> {
const { name, endpoint, headers } = config;
if (config.chainIds !== undefined && !config.chainIds.includes(request.tokenInChainId)) {
this.log.debug(
Expand Down Expand Up @@ -300,7 +300,7 @@ export class WebhookQuoter implements Quoter {
// valid non-quote responses:
// - 404
// - 0 amount quote
function isNonQuote(request: QuoteRequest, hookResponse: AxiosResponse, parsedResponse: QuoteResponse): boolean {
function isNonQuote(request: IQuoteRequest, hookResponse: AxiosResponse, parsedResponse: QuoteResponse): boolean {
if (hookResponse.status === 404) {
return true;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/quoters/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { QuoteRequest, QuoteResponse } from '../entities';
import { IQuoteRequest, QuoteResponse } from '../entities';
import { ProtocolVersion } from '../providers';

export enum QuoterType {
Expand All @@ -8,7 +8,7 @@ export enum QuoterType {
}

export interface Quoter {
quote(request: QuoteRequest, version: ProtocolVersion): Promise<QuoteResponse[]>;
quote(request: IQuoteRequest, version: ProtocolVersion): Promise<QuoteResponse[]>;
type(): QuoterType;
}

Expand Down
2 changes: 1 addition & 1 deletion test/handlers/hard-quote/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '../../../lib/handlers/hard-quote';
import { getCosignerData } from '../../../lib/handlers/hard-quote/handler';
import { MockOrderServiceProvider } from '../../../lib/providers';
import { MockQuoter, MOCK_FILLER_ADDRESS, Quoter } from '../../../lib/quoters';
import { MOCK_FILLER_ADDRESS, MockQuoter, Quoter } from '../../../lib/quoters';

jest.mock('axios');

Expand Down

0 comments on commit d0406b9

Please sign in to comment.