Skip to content

Commit

Permalink
add fee data to zap requests
Browse files Browse the repository at this point in the history
  • Loading branch information
seguido committed Sep 19, 2024
1 parent 2e555e7 commit 5589e1b
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 24 deletions.
36 changes: 29 additions & 7 deletions src/api/zap/api/kyber/KyberApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,27 @@ import {
import { mapValues, omitBy } from 'lodash';
import { redactSecrets } from '../../../../utils/secrets';
import { ApiResponse, isErrorApiResponse } from '../common';
import { ApiChain } from '../../../../utils/chain';
import { addressBook } from '../../../../../packages/address-book/src/address-book';

export class KyberApi implements IKyberApi {
constructor(protected readonly baseUrl: string, protected readonly clientId: string) {}
readonly feeReceiver: string;
readonly ZAP_FEE = 0.0005;
constructor(protected readonly baseUrl: string, protected readonly clientId: string, chain: ApiChain) {
const beefyPlatform = addressBook[chain].platforms.beefyfinance;
if (!beefyPlatform) {
throw new Error(`No Beefy platform found for chain ${chain}`);
}
this.feeReceiver =
beefyPlatform.treasurySwapper || beefyPlatform.treasuryMultisig || beefyPlatform.treasury;
}

protected buildUrl<T extends {}>(path: string, request?: T) {
const params = request ? new URLSearchParams(request).toString() : '';
return params ? `${this.baseUrl}${path}?${params}` : `${this.baseUrl}${path}`;
}

protected toStringDict(
obj: Record<string, string | number | boolean | string[]>
): Record<string, string> {
protected toStringDict(obj: Record<string, string | number | boolean | string[]>): Record<string, string> {
return mapValues(
omitBy(obj, v => v === undefined),
v => (Array.isArray(v) ? v.join(',') : String(v))
Expand All @@ -49,6 +58,20 @@ export class KyberApi implements IKyberApi {
};
}

protected withFeeReceiver(
request?: Record<string, string | number | boolean | string[]>
): Record<string, string | number | boolean | string[]> {
return this.feeReceiver && (this.ZAP_FEE || 0) > 0
? {
...request,
feeAmount: (this.ZAP_FEE * 10000).toString(10), // *10000 to bps
isInBps: true,
chargeFeeBy: 'currency_in',
feeReceiver: this.feeReceiver,
}
: request;
}

protected async doGet<ResponseType extends object>(
path: string,
request?: Record<string, string>
Expand Down Expand Up @@ -131,8 +154,7 @@ export class KyberApi implements IKyberApi {

return {
code: response.status === 200 ? 500 : response.status,
message:
response.status === 200 ? 'upstream response not json' : redactSecrets(response.statusText),
message: response.status === 200 ? 'upstream response not json' : redactSecrets(response.statusText),
};
}

Expand All @@ -157,7 +179,7 @@ export class KyberApi implements IKyberApi {
}

async getProxiedQuote(request: QuoteRequest): Promise<ApiResponse<QuoteData>> {
return await this.priorityGet<QuoteData>('/routes', this.toStringDict(request));
return await this.priorityGet<QuoteData>('/routes', this.toStringDict(this.withFeeReceiver(request)));
}

async postProxiedSwap(request: SwapRequest): Promise<ApiResponse<SwapData>> {
Expand Down
5 changes: 3 additions & 2 deletions src/api/zap/api/kyber/RateLimitedKyberApi.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { KyberApi } from './KyberApi';
import PQueue from 'p-queue';
import { ApiResponse } from '../common';
import { ApiChain } from '../../../../utils/chain';

export class RateLimitedKyberApi extends KyberApi {
constructor(baseUrl: string, clientId: string, protected readonly queue: PQueue) {
super(baseUrl, clientId);
constructor(baseUrl: string, clientId: string, protected readonly queue: PQueue, chain: ApiChain) {
super(baseUrl, clientId, chain);
}

protected async get<ResponseType extends object>(
Expand Down
2 changes: 1 addition & 1 deletion src/api/zap/api/kyber/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function getKyberApi(chain: AnyChain): IKyberApi {
throw new Error(`KYBER_CLIENT_ID env variable is not set`);
}

swapApiByChain[apiChain] = new RateLimitedKyberApi(baseUrl, clientId, swapApiQueue);
swapApiByChain[apiChain] = new RateLimitedKyberApi(baseUrl, clientId, swapApiQueue, apiChain);
}

return swapApiByChain[apiChain];
Expand Down
20 changes: 15 additions & 5 deletions src/api/zap/api/odos/OdosApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import { redactSecrets } from '../../../../utils/secrets';
import { ApiResponse, isErrorApiResponse } from '../common';

export class OdosApi implements IOdosApi {
constructor(protected readonly baseUrl: string, protected readonly chainId: number) {}
readonly referralCode: number;
constructor(protected readonly baseUrl: string, protected readonly chainId: number) {
this.referralCode = Number(process.env.ODOS_CODE || 0);
}

protected buildUrl<T extends {}>(path: string, request?: T) {
const params = request ? new URLSearchParams(request).toString() : '';
Expand All @@ -25,6 +28,13 @@ export class OdosApi implements IOdosApi {
};
}

protected withReferralCode(request?: Record<string, unknown>): Record<string, unknown> {
return {
...request,
referralCode: this.referralCode,
};
}

protected buildHeaders(additionalHeaders?: Record<string, string>): Record<string, string> {
return {
Accept: 'application/json,*/*;q=0.8',
Expand Down Expand Up @@ -102,6 +112,10 @@ export class OdosApi implements IOdosApi {
return response.data;
}

async postProxiedQuote(request: QuoteRequest): Promise<ApiResponse<QuoteResponse>> {
return await this.priorityPost<QuoteResponse>('/sor/quote/v2', this.withReferralCode(request));
}

async postSwap(request: SwapRequest): Promise<SwapResponse> {
const response = await this.post<SwapResponse>('/sor/assemble', request);

Expand All @@ -112,10 +126,6 @@ export class OdosApi implements IOdosApi {
return response.data;
}

async postProxiedQuote(request: QuoteRequest): Promise<ApiResponse<QuoteResponse>> {
return await this.priorityPost<QuoteResponse>('/sor/quote/v2', request);
}

async postProxiedSwap(request: SwapRequest): Promise<ApiResponse<SwapResponse>> {
return await this.priorityPost<SwapResponse>('/sor/assemble', request);
}
Expand Down
42 changes: 37 additions & 5 deletions src/api/zap/api/one-inch/OneInchSwapApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,20 @@ import {
import { mapValues, omitBy } from 'lodash';
import { redactSecrets } from '../../../../utils/secrets';
import { isErrorApiResponse, ApiResponse } from '../common';
import { ApiChain } from '../../../../utils/chain';
import { addressBook } from '../../../../../packages/address-book/src/address-book';

export class OneInchSwapApi implements IOneInchSwapApi {
constructor(protected readonly baseUrl: string, protected readonly apiKey: string) {}
readonly feeReceiver: string;
readonly ZAP_FEE = 0.0005;
constructor(protected readonly baseUrl: string, protected readonly apiKey: string, chain: ApiChain) {
const beefyPlatform = addressBook[chain].platforms.beefyfinance;
if (!beefyPlatform) {
throw new Error(`No Beefy platform found for chain ${chain}`);
}
this.feeReceiver =
beefyPlatform.treasurySwapper || beefyPlatform.treasuryMultisig || beefyPlatform.treasury;
}

protected buildUrl<T extends {}>(path: string, request?: T) {
let queryString: string | undefined;
Expand Down Expand Up @@ -62,8 +73,7 @@ export class OneInchSwapApi implements IOneInchSwapApi {

return {
code: response.status === 200 ? 500 : response.status,
message:
response.status === 200 ? 'upstream response not json' : redactSecrets(response.statusText),
message: response.status === 200 ? 'upstream response not json' : redactSecrets(response.statusText),
};
}

Expand All @@ -88,12 +98,34 @@ export class OneInchSwapApi implements IOneInchSwapApi {
};
}

protected withFee(
request?: Record<string, string | number | boolean>
): Record<string, string | number | boolean> {
return {
...request,
fee: (this.ZAP_FEE * 100).toString(10),
};
}

protected withFeeReferrer(
request?: Record<string, string | number | boolean>
): Record<string, string | number | boolean> {
return {
...request,
fee: (this.ZAP_FEE * 100).toString(10),
referrer: this.feeReceiver,
};
}

async getProxiedQuote(request: QuoteRequest): Promise<ApiResponse<QuoteResponse>> {
return await this.priorityGet('/quote', this.toStringDict(this.addRequiredParams(request)));
return await this.priorityGet('/quote', this.toStringDict(this.withFee(this.addRequiredParams(request))));
}

async getProxiedSwap(request: SwapRequest): Promise<ApiResponse<SwapResponse>> {
return await this.priorityGet('/swap', this.toStringDict(this.addRequiredParams(request)));
return await this.priorityGet(
'/swap',
this.toStringDict(this.withFeeReferrer(this.addRequiredParams(request)))
);
}

async getQuote(request: QuoteRequest): Promise<QuoteResponse> {
Expand Down
5 changes: 3 additions & 2 deletions src/api/zap/api/one-inch/RateLimitedOneInchSwapApi.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { OneInchSwapApi } from './OneInchSwapApi';
import PQueue from 'p-queue';
import { ApiResponse } from '../common';
import { ApiChain } from '../../../../utils/chain';

export class RateLimitedOneInchSwapApi extends OneInchSwapApi {
constructor(baseUrl: string, apiKey: string, protected readonly queue: PQueue) {
super(baseUrl, apiKey);
constructor(baseUrl: string, apiKey: string, protected readonly queue: PQueue, chain: ApiChain) {
super(baseUrl, apiKey, chain);
}

protected async get<ResponseType extends object>(
Expand Down
2 changes: 1 addition & 1 deletion src/api/zap/api/one-inch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function getOneInchSwapApi(chain: AnyChain): IOneInchSwapApi {
if (!apiKey) {
throw new Error(`ONE_INCH_API_KEY env variable is not set`);
}
swapApiByChain[apiChain] = new RateLimitedOneInchSwapApi(baseUrl, apiKey, swapApiQueue);
swapApiByChain[apiChain] = new RateLimitedOneInchSwapApi(baseUrl, apiKey, swapApiQueue, apiChain);
}

return swapApiByChain[apiChain];
Expand Down
2 changes: 1 addition & 1 deletion src/utils/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { pick, pickBy } from 'lodash';
import escapeStringRegexp from 'escape-string-regexp';

const SECRET_ENV_KEYS = ['ONE_INCH_API', 'KYBER_API', 'ODOS'];
const SECRET_ENV_SUFFIXES = ['_RPC', '_KEY', '_TOKEN', '_URL'];
const SECRET_ENV_SUFFIXES = ['_RPC', '_KEY', '_TOKEN', '_URL', '_CODE'];

const SECRETS: Record<string, string> = {
...pick(process.env, SECRET_ENV_KEYS),
Expand Down

0 comments on commit 5589e1b

Please sign in to comment.