Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make IQuoteRequest interface #312

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 45 additions & 13 deletions lib/entities/HardQuoteRequest.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
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 { v4 as uuidv4 } from 'uuid';

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

export class HardQuoteRequest {
export class HardQuoteRequest implements IQuoteRequest {
public protocol = ProtocolVersion.V2;
public order: UnsignedV2DutchOrder;
private data: HardQuoteRequestBody;

Expand All @@ -23,12 +24,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 @@ -39,9 +40,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 @@ -54,13 +62,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 @@ -97,10 +116,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 @@ -19,13 +19,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 @@ -43,7 +51,7 @@ export class QuoteRequest {

constructor(private data: QuoteRequestData) {}

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

public toCleanJSON(): QuoteRequestDataJSON {
public toCleanJSON(): ExternalRFQDataJSON {
return {
tokenInChainId: this.tokenInChainId,
tokenOutChainId: this.tokenOutChainId,
Expand All @@ -77,7 +85,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 @@ -96,7 +104,7 @@ export class QuoteRequest {
};
}

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

const bestQuote = await getBestQuote(quoters, request.toQuoteRequest(), log, metric, 'HardResponse');
const bestQuote = await getBestQuote(quoters, request, log, metric, '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 { Metric, IQuoteRequest, QuoteRequest, QuoteResponse } from '../../entities';
import { Quoter } from '../../quoters';
import { NoQuotesAvailable } from '../../util/errors';
import { timestampInMstoSeconds } from '../../util/time';
Expand Down Expand Up @@ -83,7 +83,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,
eventType: EventType = 'QuoteResponse'
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 @@ -12,8 +12,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 @@ -20,6 +19,7 @@ import { CircuitBreakerConfigurationProvider } from '../providers/circuit-breake
import { FillerComplianceConfigurationProvider } from '../providers/compliance';
import { FillerAddressRepository } from '../repositories/filler-address-repository';
import { timestampInMstoISOString } from '../util/time';
import { Quoter, QuoterType } from '.';

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

public async quote(request: QuoteRequest): Promise<QuoteResponse[]> {
public async quote(request: IQuoteRequest): Promise<QuoteResponse[]> {
let endpoints = await this.getEligibleEndpoints();
const endpointToAddrsMap = await this.complianceProvider.getEndpointToExcludedAddrsMap();
endpoints = endpoints.filter(
Expand Down Expand Up @@ -92,7 +92,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 @@ -313,7 +313,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';

export enum QuoterType {
TEST = 'TEST',
Expand All @@ -7,7 +7,7 @@ export enum QuoterType {
}

export interface Quoter {
quote(request: QuoteRequest): Promise<QuoteResponse[]>;
quote(request: IQuoteRequest): 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
Loading