@0xsequence/marketplace-sdk 0.5.3 → 0.5.5
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/{builder-types-wOwfTJpd.d.ts → builder-types-Jl3Ymws8.d.ts} +1 -1
- package/dist/chunk-3BLBZYQX.js +56 -0
- package/dist/chunk-3BLBZYQX.js.map +1 -0
- package/dist/{chunk-BZD2LDJJ.js → chunk-7C7ADZ2H.js} +2 -2
- package/dist/{chunk-H5YWG6WN.js → chunk-AXTDPTRD.js} +317 -147
- package/dist/chunk-AXTDPTRD.js.map +1 -0
- package/dist/{chunk-BVXIRVEC.js → chunk-CIPPTQDA.js} +219 -72
- package/dist/chunk-CIPPTQDA.js.map +1 -0
- package/dist/{chunk-Y7YTLAO2.js → chunk-P7UNMRZ5.js} +3 -3
- package/dist/{chunk-WSCUPAGR.js → chunk-SA3U25NU.js} +2 -1
- package/dist/{chunk-WSCUPAGR.js.map → chunk-SA3U25NU.js.map} +1 -1
- package/dist/{chunk-MWDG7UTB.js → chunk-ZBLU3Q22.js} +1 -1
- package/dist/{create-config-Bltg8Enl.d.ts → create-config-DOUq8Day.d.ts} +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +8 -8
- package/dist/{sdk-config-B32_2bG3.d.ts → marketplace.gen-D0ADxbfH.d.ts} +1 -24
- package/dist/react/_internal/api/index.d.ts +4 -2
- package/dist/react/_internal/api/index.js +1 -1
- package/dist/react/_internal/databeat/index.css +82 -0
- package/dist/react/_internal/databeat/index.css.map +1 -0
- package/dist/react/_internal/databeat/index.d.ts +68 -0
- package/dist/react/_internal/databeat/index.js +26 -0
- package/dist/react/_internal/databeat/index.js.map +1 -0
- package/dist/react/_internal/index.d.ts +6 -5
- package/dist/react/_internal/index.js +1 -1
- package/dist/react/_internal/wagmi/index.d.ts +4 -3
- package/dist/react/hooks/index.d.ts +446 -5
- package/dist/react/hooks/index.js +19 -5
- package/dist/react/index.d.ts +7 -6
- package/dist/react/index.js +22 -7
- package/dist/react/ssr/index.js +1 -0
- package/dist/react/ssr/index.js.map +1 -1
- package/dist/react/ui/components/collectible-card/index.d.ts +4 -3
- package/dist/react/ui/components/collectible-card/index.js +8 -7
- package/dist/react/ui/components/marketplace-logos/index.js +1 -1
- package/dist/react/ui/index.d.ts +4 -3
- package/dist/react/ui/index.js +8 -7
- package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +4 -3
- package/dist/react/ui/modals/_internal/components/actionModal/index.js +6 -6
- package/dist/sdk-config-xWkdBdrL.d.ts +24 -0
- package/dist/{services-BRBVE0mm.d.ts → services-Dd2MoBTM.d.ts} +2 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/index.js +1 -1
- package/dist/{types-BY3husBh.d.ts → types-vOfhbBkR.d.ts} +3 -2
- package/dist/utils/abi/index.js +5 -5
- package/dist/utils/index.d.ts +4 -3
- package/dist/utils/index.js +8 -8
- package/package.json +2 -1
- package/src/react/_internal/api/query-keys.ts +1 -0
- package/src/react/_internal/databeat/index.ts +63 -0
- package/src/react/_internal/databeat/types.ts +70 -0
- package/src/react/hooks/__tests__/useComparePrices.test.tsx +215 -0
- package/src/react/hooks/__tests__/useConvertPriceToUSD.test.tsx +173 -0
- package/src/react/hooks/__tests__/useListCollectiblesPaginated.test.tsx +217 -0
- package/src/react/hooks/index.ts +3 -0
- package/src/react/hooks/useComparePrices.tsx +106 -0
- package/src/react/hooks/useConvertPriceToUSD.tsx +102 -0
- package/src/react/hooks/useFilters.tsx +9 -3
- package/src/react/hooks/useListCollectiblesPaginated.tsx +78 -0
- package/src/react/ui/components/collectible-card/CollectibleCard.tsx +1 -0
- package/src/react/ui/components/collectible-card/Footer.tsx +10 -2
- package/src/react/ui/modals/BuyModal/hooks/useBuyCollectable.ts +30 -3
- package/src/react/ui/modals/CreateListingModal/hooks/useTransactionSteps.tsx +40 -4
- package/src/react/ui/modals/MakeOfferModal/hooks/useTransactionSteps.tsx +26 -2
- package/src/react/ui/modals/SellModal/Modal.tsx +3 -1
- package/src/react/ui/modals/SellModal/hooks/useSell.tsx +10 -7
- package/src/react/ui/modals/SellModal/hooks/useTransactionSteps.tsx +51 -13
- package/src/react/ui/modals/_internal/components/floorPriceText/index.tsx +30 -12
- package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +51 -4
- package/src/react/ui/modals/_internal/components/priceInput/index.tsx +24 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/chunk-BVXIRVEC.js.map +0 -1
- package/dist/chunk-H5YWG6WN.js.map +0 -1
- /package/dist/{chunk-BZD2LDJJ.js.map → chunk-7C7ADZ2H.js.map} +0 -0
- /package/dist/{chunk-Y7YTLAO2.js.map → chunk-P7UNMRZ5.js.map} +0 -0
- /package/dist/{chunk-MWDG7UTB.js.map → chunk-ZBLU3Q22.js.map} +0 -0
- /package/src/react/ui/modals/BuyModal/hooks/__tests__/{useBuyCollectable.test.tsx → useBuyCollectable.test.tsx.bak} +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { waitFor } from '@testing-library/react';
|
|
2
|
+
import { http, HttpResponse } from 'msw';
|
|
3
|
+
import { zeroAddress } from 'viem';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import {
|
|
6
|
+
mockCollectibleOrder,
|
|
7
|
+
mockMarketplaceEndpoint,
|
|
8
|
+
} from '../../_internal/api/__mocks__/marketplace.msw';
|
|
9
|
+
import { OrderSide } from '../../_internal/api/marketplace.gen';
|
|
10
|
+
import { renderHook } from '../../_internal/test-utils';
|
|
11
|
+
import { server } from '../../_internal/test/setup';
|
|
12
|
+
import { useListCollectiblesPaginated } from '../useListCollectiblesPaginated';
|
|
13
|
+
import type { UseListCollectiblesPaginatedArgs } from '../useListCollectiblesPaginated';
|
|
14
|
+
|
|
15
|
+
describe('useListCollectiblesPaginated', () => {
|
|
16
|
+
const defaultArgs: UseListCollectiblesPaginatedArgs = {
|
|
17
|
+
chainId: '1',
|
|
18
|
+
collectionAddress: zeroAddress,
|
|
19
|
+
side: OrderSide.listing,
|
|
20
|
+
query: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
page: 1,
|
|
23
|
+
pageSize: 30,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
it('should fetch collectibles successfully', async () => {
|
|
28
|
+
// Set up mock response
|
|
29
|
+
server.use(
|
|
30
|
+
http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
|
|
31
|
+
return HttpResponse.json({
|
|
32
|
+
collectibles: [mockCollectibleOrder],
|
|
33
|
+
page: { page: 1, pageSize: 30, more: false },
|
|
34
|
+
});
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const { result } = renderHook(() =>
|
|
39
|
+
useListCollectiblesPaginated(defaultArgs),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Wait for the query to complete
|
|
43
|
+
await waitFor(() => {
|
|
44
|
+
expect(result.current.isSuccess).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Verify the data
|
|
48
|
+
expect(result.current.data?.collectibles).toEqual([mockCollectibleOrder]);
|
|
49
|
+
expect(result.current.data?.page).toEqual({
|
|
50
|
+
page: 1,
|
|
51
|
+
pageSize: 30,
|
|
52
|
+
more: false,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should handle error states', async () => {
|
|
57
|
+
// Override the handler to return an error
|
|
58
|
+
server.use(
|
|
59
|
+
http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
|
|
60
|
+
return HttpResponse.json(
|
|
61
|
+
{ error: { message: 'Failed to fetch collectibles' } },
|
|
62
|
+
{ status: 500 },
|
|
63
|
+
);
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const { result } = renderHook(() =>
|
|
68
|
+
useListCollectiblesPaginated(defaultArgs),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Wait for the error state
|
|
72
|
+
await waitFor(() => {
|
|
73
|
+
expect(result.current.isError).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(result.current.error).toBeDefined();
|
|
77
|
+
expect(result.current.data).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle pagination correctly', async () => {
|
|
81
|
+
// Set up mock response for page 2
|
|
82
|
+
const modifiedCollectibleOrder = {
|
|
83
|
+
...mockCollectibleOrder,
|
|
84
|
+
metadata: {
|
|
85
|
+
...mockCollectibleOrder.metadata,
|
|
86
|
+
tokenId: '2',
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
server.use(
|
|
91
|
+
http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
|
|
92
|
+
return HttpResponse.json({
|
|
93
|
+
collectibles: [modifiedCollectibleOrder],
|
|
94
|
+
page: { page: 2, pageSize: 20, more: false },
|
|
95
|
+
});
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const paginatedArgs: UseListCollectiblesPaginatedArgs = {
|
|
100
|
+
...defaultArgs,
|
|
101
|
+
query: {
|
|
102
|
+
enabled: true,
|
|
103
|
+
page: 2,
|
|
104
|
+
pageSize: 20,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const { result } = renderHook(() =>
|
|
109
|
+
useListCollectiblesPaginated(paginatedArgs),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Wait for the query to complete
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
expect(result.current.isSuccess).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Verify the data for page 2
|
|
118
|
+
expect(result.current.data?.collectibles[0].metadata.tokenId).toBe('2');
|
|
119
|
+
expect(result.current.data?.page).toEqual({
|
|
120
|
+
page: 2,
|
|
121
|
+
pageSize: 20,
|
|
122
|
+
more: false,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should refetch when args change', async () => {
|
|
127
|
+
// Set up initial mock response
|
|
128
|
+
server.use(
|
|
129
|
+
http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
|
|
130
|
+
return HttpResponse.json({
|
|
131
|
+
collectibles: [mockCollectibleOrder],
|
|
132
|
+
page: { page: 1, pageSize: 30, more: true },
|
|
133
|
+
});
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
let currentArgs = defaultArgs;
|
|
138
|
+
const { result, rerender } = renderHook(() =>
|
|
139
|
+
useListCollectiblesPaginated(currentArgs),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Wait for initial data
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(result.current.isSuccess).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Change args and rerender
|
|
148
|
+
currentArgs = {
|
|
149
|
+
...defaultArgs,
|
|
150
|
+
query: {
|
|
151
|
+
...defaultArgs.query,
|
|
152
|
+
page: 2,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Set up mock response for page 2
|
|
157
|
+
const modifiedCollectibleOrder = {
|
|
158
|
+
...mockCollectibleOrder,
|
|
159
|
+
metadata: {
|
|
160
|
+
...mockCollectibleOrder.metadata,
|
|
161
|
+
tokenId: '2',
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
server.use(
|
|
166
|
+
http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
|
|
167
|
+
return HttpResponse.json({
|
|
168
|
+
collectibles: [modifiedCollectibleOrder],
|
|
169
|
+
page: { page: 2, pageSize: 30, more: false },
|
|
170
|
+
});
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
rerender();
|
|
175
|
+
|
|
176
|
+
// Wait for new data
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
expect(result.current.data?.page?.page).toBe(2);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Verify new data
|
|
182
|
+
expect(result.current.data?.collectibles[0].metadata.tokenId).toBe('2');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should not fetch when enabled is false', async () => {
|
|
186
|
+
// Set up a mock handler to ensure no requests are made
|
|
187
|
+
let requestMade = false;
|
|
188
|
+
server.use(
|
|
189
|
+
http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
|
|
190
|
+
requestMade = true;
|
|
191
|
+
return HttpResponse.json({
|
|
192
|
+
collectibles: [mockCollectibleOrder],
|
|
193
|
+
page: { page: 1, pageSize: 30, more: false },
|
|
194
|
+
});
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const disabledArgs: UseListCollectiblesPaginatedArgs = {
|
|
199
|
+
...defaultArgs,
|
|
200
|
+
query: {
|
|
201
|
+
enabled: false,
|
|
202
|
+
page: 1,
|
|
203
|
+
pageSize: 30,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const { result } = renderHook(() =>
|
|
208
|
+
useListCollectiblesPaginated(disabledArgs),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// For disabled queries, we expect no loading state and no data
|
|
212
|
+
expect(result.current.isLoading).toBe(false);
|
|
213
|
+
expect(result.current.data).toBeUndefined();
|
|
214
|
+
expect(result.current.error).toBeNull();
|
|
215
|
+
expect(requestMade).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
package/src/react/hooks/index.ts
CHANGED
|
@@ -4,7 +4,9 @@ export * from './useCountOfCollectables';
|
|
|
4
4
|
export * from './useCollectible';
|
|
5
5
|
export * from './useCollection';
|
|
6
6
|
export * from './useCollectionBalanceDetails';
|
|
7
|
+
export * from './useComparePrices';
|
|
7
8
|
export * from './useConfig';
|
|
9
|
+
export * from './useConvertPriceToUSD';
|
|
8
10
|
export * from './useCurrencies';
|
|
9
11
|
export * from './useCurrency';
|
|
10
12
|
export * from './useFilters';
|
|
@@ -13,6 +15,7 @@ export * from './useHighestOffer';
|
|
|
13
15
|
export * from './useListBalances';
|
|
14
16
|
export * from './useListCollectibleActivities';
|
|
15
17
|
export * from './useListCollectibles';
|
|
18
|
+
export * from './useListCollectiblesPaginated';
|
|
16
19
|
export * from './useListCollectionActivities';
|
|
17
20
|
export * from './useListOffersForCollectible';
|
|
18
21
|
export * from './useCountOffersForCollectible';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { queryOptions, useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SdkConfig } from '../../types';
|
|
4
|
+
import {
|
|
5
|
+
AddressSchema,
|
|
6
|
+
ChainIdSchema,
|
|
7
|
+
QueryArgSchema,
|
|
8
|
+
currencyKeys,
|
|
9
|
+
} from '../_internal';
|
|
10
|
+
import { useConfig } from './useConfig';
|
|
11
|
+
import { convertPriceToUSD } from './useConvertPriceToUSD';
|
|
12
|
+
|
|
13
|
+
const ChainIdCoerce = ChainIdSchema.transform((val) => val.toString());
|
|
14
|
+
|
|
15
|
+
const UseComparePricesArgsSchema = z.object({
|
|
16
|
+
chainId: ChainIdCoerce,
|
|
17
|
+
// First price details
|
|
18
|
+
priceAmountRaw: z.string(),
|
|
19
|
+
priceCurrencyAddress: AddressSchema,
|
|
20
|
+
// Second price details (to compare against)
|
|
21
|
+
compareToPriceAmountRaw: z.string(),
|
|
22
|
+
compareToPriceCurrencyAddress: AddressSchema,
|
|
23
|
+
query: QueryArgSchema,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
type UseComparePricesArgs = z.input<typeof UseComparePricesArgsSchema>;
|
|
27
|
+
|
|
28
|
+
export type UseComparePricesReturn = {
|
|
29
|
+
percentageDifference: number;
|
|
30
|
+
percentageDifferenceFormatted: string;
|
|
31
|
+
status: 'above' | 'same' | 'below';
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const comparePrices = async (
|
|
35
|
+
args: UseComparePricesArgs,
|
|
36
|
+
config: SdkConfig,
|
|
37
|
+
): Promise<UseComparePricesReturn> => {
|
|
38
|
+
const parsedArgs = UseComparePricesArgsSchema.parse(args);
|
|
39
|
+
const [priceUSD, compareToPriceUSD] = await Promise.all([
|
|
40
|
+
convertPriceToUSD(
|
|
41
|
+
{
|
|
42
|
+
chainId: parsedArgs.chainId,
|
|
43
|
+
currencyAddress: parsedArgs.priceCurrencyAddress,
|
|
44
|
+
amountRaw: parsedArgs.priceAmountRaw,
|
|
45
|
+
query: {},
|
|
46
|
+
},
|
|
47
|
+
config,
|
|
48
|
+
),
|
|
49
|
+
convertPriceToUSD(
|
|
50
|
+
{
|
|
51
|
+
chainId: parsedArgs.chainId,
|
|
52
|
+
currencyAddress: parsedArgs.compareToPriceCurrencyAddress,
|
|
53
|
+
amountRaw: parsedArgs.compareToPriceAmountRaw,
|
|
54
|
+
query: {},
|
|
55
|
+
},
|
|
56
|
+
config,
|
|
57
|
+
),
|
|
58
|
+
]);
|
|
59
|
+
const difference = priceUSD.usdAmount - compareToPriceUSD.usdAmount;
|
|
60
|
+
|
|
61
|
+
if (compareToPriceUSD.usdAmount === 0) {
|
|
62
|
+
throw new Error('Cannot compare to zero price');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const percentageDifference = (difference / compareToPriceUSD.usdAmount) * 100;
|
|
66
|
+
const isAbove = percentageDifference > 0;
|
|
67
|
+
const isSame = percentageDifference === 0;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
percentageDifference,
|
|
71
|
+
percentageDifferenceFormatted: Math.abs(percentageDifference).toFixed(2),
|
|
72
|
+
status: isAbove ? 'above' : isSame ? 'same' : 'below',
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const comparePricesOptions = (
|
|
77
|
+
args: UseComparePricesArgs,
|
|
78
|
+
config: SdkConfig,
|
|
79
|
+
) => {
|
|
80
|
+
return queryOptions({
|
|
81
|
+
...args.query,
|
|
82
|
+
queryKey: [...currencyKeys.conversion, 'compare', args],
|
|
83
|
+
queryFn: () => comparePrices(args, config),
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Hook to compare prices between different currencies by converting both to USD
|
|
89
|
+
* @param args - The arguments for the hook
|
|
90
|
+
* @returns The percentage difference between the two prices
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const { data } = useComparePrices({
|
|
94
|
+
* chainId: 1,
|
|
95
|
+
* priceAmountRaw: "1000000000000000000",
|
|
96
|
+
* priceCurrencyAddress: "0x0000000000000000000000000000000000000000",
|
|
97
|
+
* });
|
|
98
|
+
*
|
|
99
|
+
* console.log(data);
|
|
100
|
+
* // { percentageDifference: 10, percentageDifferenceFormatted: "10.00", isAbove: true, isSame: false, isBelow: false }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export const useComparePrices = (args: UseComparePricesArgs) => {
|
|
104
|
+
const config = useConfig();
|
|
105
|
+
return useQuery(comparePricesOptions(args, config));
|
|
106
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { queryOptions, useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { formatUnits } from 'viem';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import type { SdkConfig } from '../../types';
|
|
5
|
+
import {
|
|
6
|
+
AddressSchema,
|
|
7
|
+
ChainIdSchema,
|
|
8
|
+
QueryArgSchema,
|
|
9
|
+
currencyKeys,
|
|
10
|
+
getQueryClient,
|
|
11
|
+
} from '../_internal';
|
|
12
|
+
import { useConfig } from './useConfig';
|
|
13
|
+
import { currenciesOptions } from './useCurrencies';
|
|
14
|
+
|
|
15
|
+
const ChainIdCoerce = ChainIdSchema.transform((val) => val.toString());
|
|
16
|
+
|
|
17
|
+
const UseConvertPriceToUSDArgsSchema = z.object({
|
|
18
|
+
chainId: ChainIdCoerce,
|
|
19
|
+
currencyAddress: AddressSchema,
|
|
20
|
+
amountRaw: z.string(),
|
|
21
|
+
query: QueryArgSchema,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type UseConvertPriceToUSDArgs = z.input<
|
|
25
|
+
typeof UseConvertPriceToUSDArgsSchema
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
export type UseConvertPriceToUSDReturn = {
|
|
29
|
+
usdAmount: number;
|
|
30
|
+
usdAmountFormatted: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const convertPriceToUSD = async (
|
|
34
|
+
args: UseConvertPriceToUSDArgs,
|
|
35
|
+
config: SdkConfig,
|
|
36
|
+
): Promise<UseConvertPriceToUSDReturn> => {
|
|
37
|
+
const parsedArgs = UseConvertPriceToUSDArgsSchema.parse(args);
|
|
38
|
+
const queryClient = getQueryClient();
|
|
39
|
+
const currencies = await queryClient.fetchQuery(
|
|
40
|
+
currenciesOptions(
|
|
41
|
+
{
|
|
42
|
+
chainId: parsedArgs.chainId,
|
|
43
|
+
},
|
|
44
|
+
config,
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
const currencyDetails = currencies.find(
|
|
48
|
+
(c) =>
|
|
49
|
+
c.contractAddress.toLowerCase() ===
|
|
50
|
+
parsedArgs.currencyAddress.toLowerCase(),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (!currencyDetails) {
|
|
54
|
+
throw new Error('Currency not found');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const amountDecimal = Number(
|
|
58
|
+
formatUnits(BigInt(parsedArgs.amountRaw), currencyDetails.decimals),
|
|
59
|
+
);
|
|
60
|
+
const usdAmount = amountDecimal * currencyDetails.exchangeRate;
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
usdAmount,
|
|
64
|
+
usdAmountFormatted: usdAmount.toFixed(2),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const convertPriceToUSDOptions = (
|
|
69
|
+
args: UseConvertPriceToUSDArgs,
|
|
70
|
+
config: SdkConfig,
|
|
71
|
+
) => {
|
|
72
|
+
return queryOptions({
|
|
73
|
+
...args.query,
|
|
74
|
+
queryKey: [
|
|
75
|
+
...currencyKeys.conversion,
|
|
76
|
+
args.chainId,
|
|
77
|
+
args.currencyAddress,
|
|
78
|
+
args.amountRaw,
|
|
79
|
+
],
|
|
80
|
+
queryFn: () => convertPriceToUSD(args, config),
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Hook to convert a price amount from a specific currency to USD
|
|
86
|
+
* @returns The price amount in USD and formatted USD amount
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const { data } = useConvertPriceToUSD({
|
|
90
|
+
* chainId: 1,
|
|
91
|
+
* currencyAddress: "0x0000000000000000000000000000000000000000",
|
|
92
|
+
* amountRaw: "1000000000000000000",
|
|
93
|
+
* });
|
|
94
|
+
*
|
|
95
|
+
* console.log(data);
|
|
96
|
+
* // { usdAmount: 1000, usdAmountFormatted: "1000.00" }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export const useConvertPriceToUSD = (args: UseConvertPriceToUSDArgs) => {
|
|
100
|
+
const config = useConfig();
|
|
101
|
+
return useQuery(convertPriceToUSDOptions(args, config));
|
|
102
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { PropertyFilter } from '@0xsequence/metadata';
|
|
1
2
|
import { queryOptions, useQuery } from '@tanstack/react-query';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { FilterCondition, type SdkConfig } from '../../types';
|
|
5
|
+
import { compareAddress } from '../../utils';
|
|
4
6
|
import {
|
|
5
7
|
AddressSchema,
|
|
6
8
|
ChainIdSchema,
|
|
@@ -11,8 +13,6 @@ import {
|
|
|
11
13
|
} from '../_internal';
|
|
12
14
|
import { useConfig } from './useConfig';
|
|
13
15
|
import { marketplaceConfigOptions } from './useMarketplaceConfig';
|
|
14
|
-
import { compareAddress } from '../../utils';
|
|
15
|
-
import type { PropertyFilter } from '@0xsequence/metadata';
|
|
16
16
|
|
|
17
17
|
const UseFiltersSchema = z.object({
|
|
18
18
|
chainId: ChainIdSchema.pipe(z.coerce.string()),
|
|
@@ -45,7 +45,13 @@ export const fetchFilters = async (args: UseFiltersArgs, config: SdkConfig) => {
|
|
|
45
45
|
compareAddress(c.address, parsedArgs.collectionAddress),
|
|
46
46
|
)?.filterSettings;
|
|
47
47
|
|
|
48
|
-
if (
|
|
48
|
+
if (
|
|
49
|
+
!collectionFilters?.exclusions ||
|
|
50
|
+
collectionFilters.exclusions.length === 0 ||
|
|
51
|
+
!collectionFilters.filterOrder ||
|
|
52
|
+
collectionFilters.filterOrder.length === 0
|
|
53
|
+
)
|
|
54
|
+
return filters;
|
|
49
55
|
|
|
50
56
|
const { filterOrder, exclusions } = collectionFilters;
|
|
51
57
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { queryOptions, useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { Page, SdkConfig } from '../../types';
|
|
4
|
+
import {
|
|
5
|
+
AddressSchema,
|
|
6
|
+
ChainIdSchema,
|
|
7
|
+
type ListCollectiblesArgs,
|
|
8
|
+
collectableKeys,
|
|
9
|
+
getMarketplaceClient,
|
|
10
|
+
} from '../_internal';
|
|
11
|
+
import { listCollectiblesArgsSchema } from '../_internal/api/zod-schema';
|
|
12
|
+
import { useConfig } from './useConfig';
|
|
13
|
+
|
|
14
|
+
const UseListCollectiblesPaginatedArgsSchema = listCollectiblesArgsSchema
|
|
15
|
+
.omit({
|
|
16
|
+
contractAddress: true,
|
|
17
|
+
})
|
|
18
|
+
.extend({
|
|
19
|
+
collectionAddress: AddressSchema,
|
|
20
|
+
chainId: ChainIdSchema.pipe(z.coerce.string()),
|
|
21
|
+
query: z
|
|
22
|
+
.object({
|
|
23
|
+
enabled: z.boolean().optional(),
|
|
24
|
+
page: z.number().optional().default(1),
|
|
25
|
+
pageSize: z.number().optional().default(30),
|
|
26
|
+
})
|
|
27
|
+
.optional()
|
|
28
|
+
.default({}),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export type UseListCollectiblesPaginatedArgs = z.infer<
|
|
32
|
+
typeof UseListCollectiblesPaginatedArgsSchema
|
|
33
|
+
>;
|
|
34
|
+
|
|
35
|
+
export type UseListCollectiblesPaginatedReturn = Awaited<
|
|
36
|
+
ReturnType<typeof fetchCollectiblesPaginated>
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
const fetchCollectiblesPaginated = async (
|
|
40
|
+
args: UseListCollectiblesPaginatedArgs,
|
|
41
|
+
marketplaceClient: Awaited<ReturnType<typeof getMarketplaceClient>>,
|
|
42
|
+
) => {
|
|
43
|
+
const parsedArgs = UseListCollectiblesPaginatedArgsSchema.parse(args);
|
|
44
|
+
const page: Page = {
|
|
45
|
+
page: parsedArgs.query?.page ?? 1,
|
|
46
|
+
pageSize: parsedArgs.query?.pageSize ?? 30,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const arg = {
|
|
50
|
+
...parsedArgs,
|
|
51
|
+
contractAddress: parsedArgs.collectionAddress,
|
|
52
|
+
page,
|
|
53
|
+
} as ListCollectiblesArgs;
|
|
54
|
+
|
|
55
|
+
return marketplaceClient.listCollectibles(arg);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const listCollectiblesPaginatedOptions = (
|
|
59
|
+
args: UseListCollectiblesPaginatedArgs,
|
|
60
|
+
config: SdkConfig,
|
|
61
|
+
) => {
|
|
62
|
+
const marketplaceClient = getMarketplaceClient(
|
|
63
|
+
args.chainId as string,
|
|
64
|
+
config,
|
|
65
|
+
);
|
|
66
|
+
return queryOptions({
|
|
67
|
+
queryKey: [...collectableKeys.lists, 'paginated', args],
|
|
68
|
+
queryFn: () => fetchCollectiblesPaginated(args, marketplaceClient),
|
|
69
|
+
enabled: args.query?.enabled ?? true,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const useListCollectiblesPaginated = (
|
|
74
|
+
args: UseListCollectiblesPaginatedArgs,
|
|
75
|
+
) => {
|
|
76
|
+
const config = useConfig();
|
|
77
|
+
return useQuery(listCollectiblesPaginatedOptions(args, config));
|
|
78
|
+
};
|
|
@@ -184,6 +184,7 @@ export function CollectibleCard({
|
|
|
184
184
|
lowestListingPriceAmount={lowestListing?.order?.priceAmount}
|
|
185
185
|
lowestListingCurrency={lowestListingCurrency}
|
|
186
186
|
balance={balance}
|
|
187
|
+
decimals={collectibleMetadata?.decimals}
|
|
187
188
|
/>
|
|
188
189
|
|
|
189
190
|
{(highestOffer || lowestListing) && (
|
|
@@ -15,6 +15,7 @@ const formatPrice = (amount: string, currency: Currency): string => {
|
|
|
15
15
|
type FooterProps = {
|
|
16
16
|
name: string;
|
|
17
17
|
type?: ContractType;
|
|
18
|
+
decimals?: number;
|
|
18
19
|
onOfferClick?: () => void;
|
|
19
20
|
highestOffer?: Order;
|
|
20
21
|
lowestListingPriceAmount?: string;
|
|
@@ -25,6 +26,7 @@ type FooterProps = {
|
|
|
25
26
|
export const Footer = ({
|
|
26
27
|
name,
|
|
27
28
|
type,
|
|
29
|
+
decimals,
|
|
28
30
|
onOfferClick,
|
|
29
31
|
highestOffer,
|
|
30
32
|
lowestListingPriceAmount,
|
|
@@ -104,7 +106,11 @@ export const Footer = ({
|
|
|
104
106
|
</Text>
|
|
105
107
|
</Box>
|
|
106
108
|
|
|
107
|
-
<TokenTypeBalancePill
|
|
109
|
+
<TokenTypeBalancePill
|
|
110
|
+
balance={balance}
|
|
111
|
+
type={type as ContractType}
|
|
112
|
+
decimals={decimals}
|
|
113
|
+
/>
|
|
108
114
|
</Box>
|
|
109
115
|
);
|
|
110
116
|
};
|
|
@@ -112,14 +118,16 @@ export const Footer = ({
|
|
|
112
118
|
const TokenTypeBalancePill = ({
|
|
113
119
|
balance,
|
|
114
120
|
type,
|
|
121
|
+
decimals,
|
|
115
122
|
}: {
|
|
116
123
|
balance?: string;
|
|
117
124
|
type: ContractType;
|
|
125
|
+
decimals?: number;
|
|
118
126
|
}) => {
|
|
119
127
|
const displayText =
|
|
120
128
|
type === ContractType.ERC1155
|
|
121
129
|
? balance
|
|
122
|
-
? `Owned: ${balance}`
|
|
130
|
+
? `Owned: ${formatUnits(BigInt(balance), decimals ?? 0)}`
|
|
123
131
|
: 'ERC-1155'
|
|
124
132
|
: 'ERC-721';
|
|
125
133
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useSelectPaymentModal } from '@0xsequence/kit-checkout';
|
|
2
2
|
import type { QueryKey } from '@tanstack/react-query';
|
|
3
|
-
import type
|
|
3
|
+
import { type Hash, type Hex, zeroAddress } from 'viem';
|
|
4
4
|
import {
|
|
5
5
|
type CheckoutOptions,
|
|
6
6
|
type MarketplaceKind,
|
|
7
|
+
StepType,
|
|
7
8
|
WalletKind,
|
|
8
9
|
balanceQueries,
|
|
9
10
|
collectableKeys,
|
|
@@ -92,11 +93,37 @@ export const useBuyCollectable = ({
|
|
|
92
93
|
walletType: WalletKind.unknown,
|
|
93
94
|
});
|
|
94
95
|
|
|
96
|
+
const order = await marketplaceClient.getOrders({
|
|
97
|
+
input: [
|
|
98
|
+
{
|
|
99
|
+
orderId: input.orderId,
|
|
100
|
+
contractAddress: collectionAddress,
|
|
101
|
+
marketplace: input.marketplace,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
|
|
95
106
|
// these states are necessary to manage appearance of the quantity modal
|
|
96
107
|
setCheckoutModalLoaded(true);
|
|
97
108
|
setCheckoutModalIsLoading(false);
|
|
98
109
|
|
|
99
|
-
const step = steps
|
|
110
|
+
const step = steps.find((step) => step.id === StepType.buy);
|
|
111
|
+
|
|
112
|
+
if (!step) {
|
|
113
|
+
throw new Error('Buy step not found');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const feesBps = BigInt(fees.amount);
|
|
117
|
+
let price = String(
|
|
118
|
+
(BigInt(order.orders[0].priceAmount) *
|
|
119
|
+
BigInt(input.quantity) *
|
|
120
|
+
(10000n + feesBps)) /
|
|
121
|
+
10000n,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (order.orders[0].priceCurrencyAddress !== zeroAddress) {
|
|
125
|
+
price = '0';
|
|
126
|
+
}
|
|
100
127
|
|
|
101
128
|
openSelectPaymentModal({
|
|
102
129
|
chain: chainId,
|
|
@@ -108,7 +135,7 @@ export const useBuyCollectable = ({
|
|
|
108
135
|
},
|
|
109
136
|
],
|
|
110
137
|
currencyAddress: priceCurrencyAddress,
|
|
111
|
-
price
|
|
138
|
+
price,
|
|
112
139
|
targetContractAddress: step.to,
|
|
113
140
|
txData: step.data as Hex,
|
|
114
141
|
collectionAddress,
|