Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
9 changes: 6 additions & 3 deletions packages/corsair/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ export const BaseProviders = [
'agenty',
'ahrefs',
'aimlapi',
'allimagesai',
'airtable',
'alchemy',
'algolia',
'allimagesai',
'alphavantage',
'altoviz',
'alttextai',
Expand Down Expand Up @@ -175,6 +175,7 @@ export const BaseProviders = [
'twochat',
'typeform',
'unione',
'uniswapapi',
'vapi',
'vercel',
'webflow',
Expand Down Expand Up @@ -205,10 +206,10 @@ export const ProviderDisplayNames = {
agenty: 'Agenty',
ahrefs: 'Ahrefs',
aimlapi: 'AI/ML API',
allimagesai: 'All Images AI',
airtable: 'Airtable',
alchemy: 'Alchemy',
algolia: 'Algolia',
allimagesai: 'All Images AI',
alphavantage: 'Alpha Vantage',
altoviz: 'Altoviz',
alttextai: 'AltText.ai',
Expand Down Expand Up @@ -352,6 +353,7 @@ export const ProviderDisplayNames = {
twochat: 'TwoChat',
typeform: 'Typeform',
unione: 'Unione',
uniswapapi: 'UniswapApi',
vapi: 'Vapi',
vercel: 'Vercel',
webflow: 'Webflow',
Expand Down Expand Up @@ -389,10 +391,10 @@ export type AllProviders =
| 'agenty'
| 'ahrefs'
| 'aimlapi'
| 'allimagesai'
| 'airtable'
| 'alchemy'
| 'algolia'
| 'allimagesai'
| 'alphavantage'
| 'altoviz'
| 'alttextai'
Expand Down Expand Up @@ -536,6 +538,7 @@ export type AllProviders =
| 'twochat'
| 'typeform'
| 'unione'
| 'uniswapapi'
| 'vapi'
| 'vercel'
| 'webflow'
Expand Down
84 changes: 84 additions & 0 deletions packages/uniswapapi/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { ApiRequestOptions, OpenAPIConfig } from 'corsair/http';
import { ApiError, request } from 'corsair/http';

export class UniswapApiAPIError extends Error {
constructor(
message: string,
public readonly code?: string,
) {
super(message);
this.name = 'UniswapApiAPIError';
}
}

const UNISWAPAPI_API_BASE = 'https://trade-api.gateway.uniswap.org';

export async function makeUniswapApiRequest<T>(
endpoint: string,
apiKey: string,
options: {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
body?: Record<string, unknown>;
query?: Record<string, string | number | boolean | undefined>;
} = {},
): Promise<T> {
const { method = 'GET', body, query } = options;

const config: OpenAPIConfig = {
BASE: UNISWAPAPI_API_BASE,
VERSION: '1.0.0',
WITH_CREDENTIALS: false,
CREDENTIALS: 'omit',
TOKEN: apiKey,
HEADERS: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'x-permit2-disabled': 'false',
},
};

const requestOptions: ApiRequestOptions = {
method,
url: endpoint,
body:
method === 'POST' || method === 'PUT' || method === 'PATCH'
? body
: undefined,
Comment thread
yuvanvk marked this conversation as resolved.
mediaType: 'application/json; charset=utf-8',
query: method === 'GET' ? query : undefined,
};

try {
return await request<T>(config, requestOptions);
} catch (error) {
if (error instanceof ApiError) {
// UniswapApi error responses use { errorCode, detail } instead of the
// generic { code, message } shape — extract those fields explicitly,
// falling back to error.message / error.status if the body doesn't match.
const body = error.body;

const message =
typeof body === 'object' &&
body !== null &&
'detail' in body &&
typeof body.detail === 'string'
? body.detail
: error.message;

const code =
typeof body === 'object' &&
body !== null &&
'errorCode' in body &&
typeof body.errorCode === 'string'
? body.errorCode
: error.status?.toString();
throw new UniswapApiAPIError(message, code);
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}

if (error instanceof Error) {
throw new UniswapApiAPIError(error.message);
}

throw new UniswapApiAPIError('Unknown error');
}
}
29 changes: 29 additions & 0 deletions packages/uniswapapi/endpoints/approval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { logEventFromContext } from 'corsair/core';
import type { UniswapApiEndpoints } from '..';
import { makeUniswapApiRequest } from '../client';
import type { UniswapApiEndpointOutputs } from './types';

export const check: UniswapApiEndpoints['approvalCheck'] = async (
ctx,
input,
) => {
const response = await makeUniswapApiRequest<
UniswapApiEndpointOutputs['approvalCheck']
>('/v1/check_approval', ctx.key, {
method: 'POST',
body: {
token: input.token,
amount: input.amount,
walletAddress: input.walletAddress,
chainId: input.chainId,
},
});

await logEventFromContext(
ctx,
'uniswapapi.approval.check',
{ ...input },
'completed',
);
return response;
};
27 changes: 27 additions & 0 deletions packages/uniswapapi/endpoints/delegation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { logEventFromContext } from 'corsair/core';
import type { UniswapApiEndpoints } from '..';
import { makeUniswapApiRequest } from '../client';
import type { UniswapApiEndpointOutputs } from './types';

export const check: UniswapApiEndpoints['delegationCheck'] = async (
ctx,
input,
) => {
const response = await makeUniswapApiRequest<
UniswapApiEndpointOutputs['delegationCheck']
>('/v1/check_delegation', ctx.key, {
method: 'POST',
body: {
walletAddress: input.walletAddress,
chainIds: input.chainIds,
},
});

await logEventFromContext(
ctx,
'uniswapapi.delegation.check',
{ ...input },
'completed',
);
return response;
};
Loading
Loading