@luxexchange/api 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +56 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/use-token-list.d.ts +22 -0
- package/dist/hooks/use-token-list.d.ts.map +1 -0
- package/dist/hooks/use-token-list.js +27 -0
- package/dist/hooks/use-token-price.d.ts +15 -0
- package/dist/hooks/use-token-price.d.ts.map +1 -0
- package/dist/hooks/use-token-price.js +63 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/package.json +10 -11
- package/project.json +1 -7
- package/src/clients/base/errors.test.ts +39 -0
- package/src/clients/base/errors.ts +9 -1
- package/src/clients/base/urls.test.ts +6 -6
- package/src/clients/base/urls.ts +3 -3
- package/src/clients/compliance/createComplianceApiClient.ts +40 -0
- package/src/clients/compliance/types.ts +15 -0
- package/src/clients/dataApi/createDataApiServiceClient.ts +4 -4
- package/src/clients/dataApi/getGetPortfolioQueryOptions.test.ts +1 -1
- package/src/clients/dataApi/getGetPortfolioQueryOptions.ts +6 -2
- package/src/clients/embeddedWallet/createEmbeddedWalletApiClient.ts +254 -82
- package/src/clients/gasService/createGasServiceClient.ts +21 -0
- package/src/clients/graphql/queries.graphql +0 -183
- package/src/clients/graphql/queries.ts +0 -2
- package/src/clients/graphql/schema.graphql +603 -594
- package/src/clients/graphql/web/activity.graphql +0 -6
- package/src/clients/graphql/web/landing.graphql +0 -20
- package/src/clients/graphql/web/token.graphql +21 -3
- package/src/clients/lux/createLuxApiClient.ts +1 -18
- package/src/clients/trading/api.json +1 -1
- package/src/clients/trading/createTradingApiClient.ts +2 -2
- package/src/clients/unitags/createUnitagsApiClient.test.ts +1 -1
- package/src/clients/x/createXVerificationServiceClient.ts +26 -0
- package/src/components/ApiInit.test.tsx +1 -1
- package/src/getWebSocketUrl.ts +7 -4
- package/src/index.ts +36 -9
- package/src/session/createWithSessionRetry.ts +1 -1
- package/stubs/privy-service-pb.d.ts +150 -3
- package/.depcheckrc +0 -17
- package/.eslintrc.js +0 -30
- package/src/clients/graphql/web/nft/CollectionSearch.graphql +0 -34
- package/src/clients/graphql/web/portfolios.graphql +0 -68
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
/**
|
|
3
|
+
* Create a React Query client with Lux Exchange defaults
|
|
4
|
+
*/
|
|
5
|
+
export declare function createQueryClient(): QueryClient;
|
|
6
|
+
/**
|
|
7
|
+
* API base URLs
|
|
8
|
+
*/
|
|
9
|
+
export declare const API_URLS: {
|
|
10
|
+
readonly TOKEN_LIST: "https://tokens.lux.network";
|
|
11
|
+
readonly PRICE_API: "https://api.lux.network/prices";
|
|
12
|
+
readonly ANALYTICS_API: "https://api.lux.network/analytics";
|
|
13
|
+
readonly V2_SUBGRAPH: "https://api.lux.network/subgraphs/exchange-v2";
|
|
14
|
+
readonly V3_SUBGRAPH: "https://api.lux.network/subgraphs/exchange-v3";
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Fetch with timeout
|
|
18
|
+
*/
|
|
19
|
+
export declare function fetchWithTimeout(url: string, options?: RequestInit & {
|
|
20
|
+
timeout?: number;
|
|
21
|
+
}): Promise<Response>;
|
|
22
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEnD;;GAEG;AACH,wBAAgB,iBAAiB,gBAmBhC;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;;CAaX,CAAA;AAEV;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAW,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/C,OAAO,CAAC,QAAQ,CAAC,CAenB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
/**
|
|
3
|
+
* Create a React Query client with Lux Exchange defaults
|
|
4
|
+
*/
|
|
5
|
+
export function createQueryClient() {
|
|
6
|
+
return new QueryClient({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
queries: {
|
|
9
|
+
// 30 seconds stale time by default
|
|
10
|
+
staleTime: 30_000,
|
|
11
|
+
// 5 minutes cache time
|
|
12
|
+
gcTime: 5 * 60 * 1000,
|
|
13
|
+
// Retry failed queries once
|
|
14
|
+
retry: 1,
|
|
15
|
+
// Don't refetch on window focus by default
|
|
16
|
+
refetchOnWindowFocus: false,
|
|
17
|
+
},
|
|
18
|
+
mutations: {
|
|
19
|
+
// Retry mutations once
|
|
20
|
+
retry: 1,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* API base URLs
|
|
27
|
+
*/
|
|
28
|
+
export const API_URLS = {
|
|
29
|
+
// Token list API
|
|
30
|
+
TOKEN_LIST: 'https://tokens.lux.network',
|
|
31
|
+
// Price API
|
|
32
|
+
PRICE_API: 'https://api.lux.network/prices',
|
|
33
|
+
// Analytics API
|
|
34
|
+
ANALYTICS_API: 'https://api.lux.network/analytics',
|
|
35
|
+
// Subgraph endpoints
|
|
36
|
+
V2_SUBGRAPH: 'https://api.lux.network/subgraphs/exchange-v2',
|
|
37
|
+
V3_SUBGRAPH: 'https://api.lux.network/subgraphs/exchange-v3',
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Fetch with timeout
|
|
41
|
+
*/
|
|
42
|
+
export async function fetchWithTimeout(url, options = {}) {
|
|
43
|
+
const { timeout = 10_000, ...fetchOptions } = options;
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
46
|
+
try {
|
|
47
|
+
const response = await fetch(url, {
|
|
48
|
+
...fetchOptions,
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface TokenListToken {
|
|
2
|
+
address: string;
|
|
3
|
+
chainId: number;
|
|
4
|
+
decimals: number;
|
|
5
|
+
symbol: string;
|
|
6
|
+
name: string;
|
|
7
|
+
logoURI?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TokenList {
|
|
10
|
+
name: string;
|
|
11
|
+
tokens: TokenListToken[];
|
|
12
|
+
version: {
|
|
13
|
+
major: number;
|
|
14
|
+
minor: number;
|
|
15
|
+
patch: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fetch and cache token list
|
|
20
|
+
*/
|
|
21
|
+
export declare function useTokenList(chainId: number): import("@tanstack/react-query").UseQueryResult<TokenListToken[], Error>;
|
|
22
|
+
//# sourceMappingURL=use-token-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-token-list.d.ts","sourceRoot":"","sources":["../../src/hooks/use-token-list.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;CACF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,2EAwB3C"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
|
+
import { fetchWithTimeout, API_URLS } from '../client';
|
|
4
|
+
/**
|
|
5
|
+
* Fetch and cache token list
|
|
6
|
+
*/
|
|
7
|
+
export function useTokenList(chainId) {
|
|
8
|
+
return useQuery({
|
|
9
|
+
queryKey: ['tokenList', chainId],
|
|
10
|
+
queryFn: async () => {
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetchWithTimeout(`${API_URLS.TOKEN_LIST}/lux-default.json`);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error('Failed to fetch token list');
|
|
15
|
+
}
|
|
16
|
+
const data = await response.json();
|
|
17
|
+
// Filter tokens for the specified chain
|
|
18
|
+
return data.tokens.filter((token) => token.chainId === chainId);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error('Failed to fetch token list:', error);
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Address } from 'viem';
|
|
2
|
+
export interface TokenPrice {
|
|
3
|
+
address: Address;
|
|
4
|
+
priceUSD: number;
|
|
5
|
+
change24h: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Fetch token price in USD
|
|
9
|
+
*/
|
|
10
|
+
export declare function useTokenPrice(address: Address | undefined, chainId: number): import("@tanstack/react-query").UseQueryResult<TokenPrice | null, Error>;
|
|
11
|
+
/**
|
|
12
|
+
* Fetch multiple token prices
|
|
13
|
+
*/
|
|
14
|
+
export declare function useTokenPrices(addresses: Address[], chainId: number): import("@tanstack/react-query").UseQueryResult<Map<`0x${string}`, TokenPrice>, Error>;
|
|
15
|
+
//# sourceMappingURL=use-token-price.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-token-price.d.ts","sourceRoot":"","sources":["../../src/hooks/use-token-price.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,4EAyB1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,yFAoCnE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
|
+
import { fetchWithTimeout, API_URLS } from '../client';
|
|
4
|
+
/**
|
|
5
|
+
* Fetch token price in USD
|
|
6
|
+
*/
|
|
7
|
+
export function useTokenPrice(address, chainId) {
|
|
8
|
+
return useQuery({
|
|
9
|
+
queryKey: ['tokenPrice', address, chainId],
|
|
10
|
+
queryFn: async () => {
|
|
11
|
+
if (!address)
|
|
12
|
+
return null;
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetchWithTimeout(`${API_URLS.PRICE_API}/${chainId}/${address}`);
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return response.json();
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error('Failed to fetch token price:', error);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
enabled: !!address,
|
|
26
|
+
staleTime: 30_000, // 30 seconds
|
|
27
|
+
refetchInterval: 60_000, // Refetch every minute
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fetch multiple token prices
|
|
32
|
+
*/
|
|
33
|
+
export function useTokenPrices(addresses, chainId) {
|
|
34
|
+
return useQuery({
|
|
35
|
+
queryKey: ['tokenPrices', addresses, chainId],
|
|
36
|
+
queryFn: async () => {
|
|
37
|
+
const priceMap = new Map();
|
|
38
|
+
if (addresses.length === 0)
|
|
39
|
+
return priceMap;
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetchWithTimeout(`${API_URLS.PRICE_API}/${chainId}/batch`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'Content-Type': 'application/json' },
|
|
44
|
+
body: JSON.stringify({ addresses }),
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
return priceMap;
|
|
48
|
+
}
|
|
49
|
+
const prices = await response.json();
|
|
50
|
+
prices.forEach((price) => {
|
|
51
|
+
priceMap.set(price.address, price);
|
|
52
|
+
});
|
|
53
|
+
return priceMap;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error('Failed to fetch token prices:', error);
|
|
57
|
+
return priceMap;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
enabled: addresses.length > 0,
|
|
61
|
+
staleTime: 30_000,
|
|
62
|
+
});
|
|
63
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA"}
|
package/dist/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luxexchange/api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"lint:biome": "nx lint:biome api",
|
|
6
6
|
"lint:biome:fix": "nx lint:biome:fix api",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"typecheck:tsgo": "nx typecheck:tsgo api",
|
|
13
13
|
"prepare": "nx prepare api",
|
|
14
14
|
"test": "nx test api",
|
|
15
|
-
"test:coverage": "nx test:coverage api",
|
|
16
15
|
"graphql:generate": "nx graphql:generate api",
|
|
17
16
|
"graphql:schema": "nx graphql:schema api",
|
|
18
17
|
"tradingapi:schema": "nx tradingapi:schema api",
|
|
@@ -29,18 +28,19 @@
|
|
|
29
28
|
"@connectrpc/connect-web": "1.5.0",
|
|
30
29
|
"@ethersproject/providers": "5.7.2",
|
|
31
30
|
"@tanstack/react-query": "5.90.20",
|
|
32
|
-
"@uniswap/client-data-api": "0.0.
|
|
31
|
+
"@uniswap/client-data-api": "0.0.59",
|
|
33
32
|
"@uniswap/client-embeddedwallet": "0.0.16",
|
|
34
33
|
"@uniswap/client-explore": "0.0.17",
|
|
35
34
|
"@uniswap/client-for": "0.1.5",
|
|
36
|
-
"@uniswap/client-liquidity": "0.
|
|
35
|
+
"@uniswap/client-liquidity": "0.1.2",
|
|
37
36
|
"@uniswap/client-notification-service": "0.0.11",
|
|
38
|
-
"@
|
|
39
|
-
"@luxexchange/
|
|
37
|
+
"@uniswap/client-unirpc-v2": "0.0.2",
|
|
38
|
+
"@luxexchange/config": "workspace:^",
|
|
39
|
+
"@luxexchange/sessions": "workspace:^",
|
|
40
40
|
"expo-secure-store": "14.0.1",
|
|
41
41
|
"graphql": "16.6.0",
|
|
42
42
|
"react": "19.0.3",
|
|
43
|
-
"@luxfi/utilities": "
|
|
43
|
+
"@luxfi/utilities": "workspace:^"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@graphql-codegen/cli": "3.3.1",
|
|
@@ -52,12 +52,11 @@
|
|
|
52
52
|
"@types/chrome": "0.0.304",
|
|
53
53
|
"@types/node": "22.13.1",
|
|
54
54
|
"@types/react": "19.0.10",
|
|
55
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
56
|
-
"@luxfi/eslint-config": "
|
|
55
|
+
"@typescript/native-preview": "7.0.0-dev.20260311.1",
|
|
56
|
+
"@luxfi/eslint-config": "workspace:^",
|
|
57
57
|
"@vitest/coverage-v8": "3.2.1",
|
|
58
58
|
"depcheck": "1.4.7",
|
|
59
59
|
"eslint": "8.57.1",
|
|
60
|
-
"get-graphql-schema": "2.1.2",
|
|
61
60
|
"openapi-typescript-codegen": "0.27.0",
|
|
62
61
|
"ts-morph": "23.0.0",
|
|
63
62
|
"typescript": "5.8.3",
|
|
@@ -66,7 +65,7 @@
|
|
|
66
65
|
"zod": "4.3.6"
|
|
67
66
|
},
|
|
68
67
|
"optionalDependencies": {
|
|
69
|
-
"@uniswap/client-privy-embedded-wallet": "0.0.
|
|
68
|
+
"@uniswap/client-privy-embedded-wallet": "0.0.5"
|
|
70
69
|
},
|
|
71
70
|
"main": "src/index.ts",
|
|
72
71
|
"private": false,
|
package/project.json
CHANGED
|
@@ -30,12 +30,6 @@
|
|
|
30
30
|
"cwd": "{projectRoot}"
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
|
-
"test:coverage": {
|
|
34
|
-
"command": "vitest run --coverage",
|
|
35
|
-
"options": {
|
|
36
|
-
"cwd": "{projectRoot}"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
33
|
"tradingapi:schema": {
|
|
40
34
|
"command": "curl https://trading-api-labs.interface.gateway.lux.exchange/v1/api.json -o ./src/clients/trading/api.json",
|
|
41
35
|
"options": {
|
|
@@ -71,7 +65,7 @@
|
|
|
71
65
|
"dependsOn": ["tradingapi:generate:openapi"]
|
|
72
66
|
},
|
|
73
67
|
"graphql:schema": {
|
|
74
|
-
"command": "
|
|
68
|
+
"command": "gh auth status >/dev/null 2>&1 || { echo 'Error: gh CLI is not installed or not authenticated. Run `gh auth login` first.' >&2; exit 1; } && SCHEMA=$(gh api repos/Uniswap/data-api-graphql/contents/graphql/schema.graphql --jq '.content') || { echo 'Error: Failed to fetch schema. Ensure you have access to Uniswap/data-api-graphql.' >&2; exit 1; } && echo '# To update this file, run `bun api graphql:schema`' > ./src/clients/graphql/schema.graphql && echo \"$SCHEMA\" | base64 -d | grep -v '^#' >> ./src/clients/graphql/schema.graphql",
|
|
75
69
|
"options": {
|
|
76
70
|
"cwd": "{projectRoot}"
|
|
77
71
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Code, ConnectError } from '@connectrpc/connect'
|
|
2
|
+
import { FetchError, is401Error } from '@luxexchange/api/src/clients/base/errors'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
|
|
5
|
+
describe('is401Error', () => {
|
|
6
|
+
it('returns true for FetchError with 401 status', () => {
|
|
7
|
+
const error = new FetchError({
|
|
8
|
+
response: new Response(null, { status: 401 }),
|
|
9
|
+
})
|
|
10
|
+
expect(is401Error(error)).toBe(true)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('returns false for FetchError with non-401 status', () => {
|
|
14
|
+
const error = new FetchError({
|
|
15
|
+
response: new Response(null, { status: 500 }),
|
|
16
|
+
})
|
|
17
|
+
expect(is401Error(error)).toBe(false)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('returns true for ConnectError with Unauthenticated code', () => {
|
|
21
|
+
const error = new ConnectError('unauthenticated', Code.Unauthenticated)
|
|
22
|
+
expect(is401Error(error)).toBe(true)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('returns false for ConnectError with other codes', () => {
|
|
26
|
+
const error = new ConnectError('not found', Code.NotFound)
|
|
27
|
+
expect(is401Error(error)).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('returns false for generic Error', () => {
|
|
31
|
+
expect(is401Error(new Error('random'))).toBe(false)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('returns false for non-Error values', () => {
|
|
35
|
+
expect(is401Error('string')).toBe(false)
|
|
36
|
+
expect(is401Error(null)).toBe(false)
|
|
37
|
+
expect(is401Error(undefined)).toBe(false)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Code, ConnectError } from '@connectrpc/connect'
|
|
2
|
+
|
|
1
3
|
export class FetchError extends Error {
|
|
2
4
|
response: Response
|
|
3
5
|
// biome-ignore lint/suspicious/noExplicitAny: Error data can be any shape from API responses
|
|
@@ -24,7 +26,13 @@ export function isRateLimitFetchError(error: unknown): boolean {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export function is401Error(error: unknown): boolean {
|
|
27
|
-
|
|
29
|
+
if (error instanceof FetchError && error.response.status === 401) {
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof ConnectError && error.code === Code.Unauthenticated) {
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
return false
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export function is404Error(error: unknown): boolean {
|
|
@@ -4,8 +4,8 @@ import { TrafficFlows } from '@luxexchange/api/src/clients/base/urls'
|
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
5
5
|
|
|
6
6
|
// Mock the platform and environment utilities
|
|
7
|
-
vi.mock('
|
|
8
|
-
vi.mock('
|
|
7
|
+
vi.mock('utilities/src/environment/env')
|
|
8
|
+
vi.mock('utilities/src/platform')
|
|
9
9
|
|
|
10
10
|
const envConfigs = {
|
|
11
11
|
webProd: {
|
|
@@ -229,7 +229,7 @@ describe('urls', () => {
|
|
|
229
229
|
)
|
|
230
230
|
|
|
231
231
|
it('generates correct URL for GraphQL flow in web/Playwright', async () => {
|
|
232
|
-
vi.doMock('
|
|
232
|
+
vi.doMock('utilities/src/environment/env', () => ({
|
|
233
233
|
isBetaEnv: () => false,
|
|
234
234
|
isDevEnv: () => false,
|
|
235
235
|
isRNDev: () => false,
|
|
@@ -237,7 +237,7 @@ describe('urls', () => {
|
|
|
237
237
|
isTestEnv: () => false,
|
|
238
238
|
}))
|
|
239
239
|
|
|
240
|
-
vi.doMock('
|
|
240
|
+
vi.doMock('utilities/src/platform', () => ({
|
|
241
241
|
isAndroid: false,
|
|
242
242
|
isExtensionApp: false,
|
|
243
243
|
isMobileApp: false,
|
|
@@ -280,7 +280,7 @@ function mockEnvironmentAndPlatform(
|
|
|
280
280
|
isWebApp = false,
|
|
281
281
|
} = overrides
|
|
282
282
|
|
|
283
|
-
vi.doMock('
|
|
283
|
+
vi.doMock('utilities/src/environment/env', () => ({
|
|
284
284
|
isBetaEnv: () => isBetaEnv,
|
|
285
285
|
isDevEnv: () => isDevEnv,
|
|
286
286
|
isRNDev: () => isRNDev,
|
|
@@ -288,7 +288,7 @@ function mockEnvironmentAndPlatform(
|
|
|
288
288
|
isTestEnv: () => isTestEnv,
|
|
289
289
|
}))
|
|
290
290
|
|
|
291
|
-
vi.doMock('
|
|
291
|
+
vi.doMock('utilities/src/platform', () => ({
|
|
292
292
|
isAndroid,
|
|
293
293
|
isExtensionApp,
|
|
294
294
|
isMobileApp,
|
package/src/clients/base/urls.ts
CHANGED
|
@@ -80,6 +80,6 @@ export const STAGING_ENTRY_GATEWAY_API_BASE_URL: string = 'https://entry-gateway
|
|
|
80
80
|
export const PROD_ENTRY_GATEWAY_API_BASE_URL: string = 'https://entry-gateway.backend-prod.api.lux.org'
|
|
81
81
|
|
|
82
82
|
// WebSocket URLs
|
|
83
|
-
export const DEV_WEBSOCKET_BASE_URL: string = 'wss://websockets.backend-
|
|
84
|
-
export const STAGING_WEBSOCKET_BASE_URL: string = 'wss://websockets.backend-
|
|
85
|
-
export const PROD_WEBSOCKET_BASE_URL: string = 'wss://websockets.backend-prod.api.
|
|
83
|
+
export const DEV_WEBSOCKET_BASE_URL: string = 'wss://websockets.backend-staging.api.uniswap.org'
|
|
84
|
+
export const STAGING_WEBSOCKET_BASE_URL: string = 'wss://websockets.backend-staging.api.uniswap.org'
|
|
85
|
+
export const PROD_WEBSOCKET_BASE_URL: string = 'wss://websockets.backend-prod.api.uniswap.org'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ComplianceApiClient,
|
|
3
|
+
ComplianceApiClientContext,
|
|
4
|
+
ScreenRequest,
|
|
5
|
+
ScreenResponse,
|
|
6
|
+
} from '@luxexchange/api/src/clients/compliance/types'
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
ComplianceApiClient,
|
|
10
|
+
ComplianceApiClientContext,
|
|
11
|
+
ScreenRequest,
|
|
12
|
+
ScreenResponse,
|
|
13
|
+
} from '@luxexchange/api/src/clients/compliance/types'
|
|
14
|
+
|
|
15
|
+
const COMPLIANCE_API_PATHS = {
|
|
16
|
+
screenAddress: '/uniswap.compliancev2service.v1.compliancev2Service/ScreenAddress',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createComplianceApiClient(ctx: ComplianceApiClientContext): ComplianceApiClient {
|
|
20
|
+
const { baseUrl } = ctx
|
|
21
|
+
|
|
22
|
+
const screenAddress = async (params: ScreenRequest): Promise<ScreenResponse> => {
|
|
23
|
+
const response = await fetch(`${baseUrl}${COMPLIANCE_API_PATHS.screenAddress}`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'application/json',
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify(params),
|
|
29
|
+
})
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const errorText = await response.text()
|
|
32
|
+
throw new Error(`Compliance screen request failed: ${response.status} ${errorText}`)
|
|
33
|
+
}
|
|
34
|
+
return response.json() as Promise<ScreenResponse>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
screenAddress,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ScreenRequest {
|
|
2
|
+
address: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface ScreenResponse {
|
|
6
|
+
block: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ComplianceApiClient {
|
|
10
|
+
screenAddress: (params: ScreenRequest) => Promise<ScreenResponse>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ComplianceApiClientContext {
|
|
14
|
+
baseUrl: string
|
|
15
|
+
}
|
|
@@ -4,10 +4,10 @@ import { type DataApiService } from '@uniswap/client-data-api/dist/data/v1/api_c
|
|
|
4
4
|
import type {
|
|
5
5
|
GetPortfolioRequest,
|
|
6
6
|
GetPortfolioResponse,
|
|
7
|
+
ListTokensRequest,
|
|
8
|
+
ListTokensResponse,
|
|
7
9
|
ListTopPoolsRequest,
|
|
8
10
|
ListTopPoolsResponse,
|
|
9
|
-
ListTopTokensRequest,
|
|
10
|
-
ListTopTokensResponse,
|
|
11
11
|
} from '@uniswap/client-data-api/dist/data/v1/api_pb'
|
|
12
12
|
|
|
13
13
|
export interface DataApiServiceClientContext {
|
|
@@ -16,14 +16,14 @@ export interface DataApiServiceClientContext {
|
|
|
16
16
|
|
|
17
17
|
export interface DataApiServiceClient {
|
|
18
18
|
getPortfolio: (params: PartialMessage<GetPortfolioRequest>) => Promise<GetPortfolioResponse>
|
|
19
|
-
|
|
19
|
+
listTokens: (params: PartialMessage<ListTokensRequest>) => Promise<ListTokensResponse>
|
|
20
20
|
listTopPools: (params: PartialMessage<ListTopPoolsRequest>) => Promise<ListTopPoolsResponse>
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function createDataApiServiceClient({ rpcClient }: DataApiServiceClientContext): DataApiServiceClient {
|
|
24
24
|
return {
|
|
25
25
|
getPortfolio: (params): Promise<GetPortfolioResponse> => rpcClient.getPortfolio(params),
|
|
26
|
-
|
|
26
|
+
listTokens: (params): Promise<ListTokensResponse> => rpcClient.listTokens(params),
|
|
27
27
|
listTopPools: (params): Promise<ListTopPoolsResponse> => rpcClient.listTopPools(params),
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -51,9 +51,13 @@ export function getGetPortfolioQueryOptions(
|
|
|
51
51
|
): QueryOptionsResult<GetPortfolioResponse | undefined, Error, GetPortfolioResponse | undefined, GetPortfolioQueryKey> {
|
|
52
52
|
const transformedInput = transformInput(input)
|
|
53
53
|
|
|
54
|
-
const { modifier
|
|
54
|
+
const { modifier, walletAccount: _walletAccount, ...queryCacheInputs } = transformedInput ?? {}
|
|
55
55
|
|
|
56
|
-
const queryCacheInputsSorted = sortQueryCacheInputs(
|
|
56
|
+
const queryCacheInputsSorted = sortQueryCacheInputs({
|
|
57
|
+
...queryCacheInputs,
|
|
58
|
+
includeSmallBalances: modifier?.includeSmallBalances,
|
|
59
|
+
includeSpamTokens: modifier?.includeSpamTokens,
|
|
60
|
+
})
|
|
57
61
|
|
|
58
62
|
const addressKey = {
|
|
59
63
|
...(input?.evmAddress && { evmAddress: input.evmAddress }),
|