@0xsequence/marketplace-sdk 0.4.7 → 0.4.9
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/{chunk-6R4G7J6Q.js → chunk-7HWJ4DUX.js} +29 -5
- package/dist/chunk-7HWJ4DUX.js.map +1 -0
- package/dist/{chunk-5UCKYAMR.js → chunk-BJLOO4NP.js} +647 -376
- package/dist/chunk-BJLOO4NP.js.map +1 -0
- package/dist/{chunk-2OJB35FS.js → chunk-BMWCIHCB.js} +5 -5
- package/dist/{chunk-2OJB35FS.js.map → chunk-BMWCIHCB.js.map} +1 -1
- package/dist/{chunk-R7GVMKMM.js → chunk-CUA4SGWT.js} +540 -18
- package/dist/chunk-CUA4SGWT.js.map +1 -0
- package/dist/{chunk-ZEH4JI2U.js → chunk-FCF57DZI.js} +7 -2
- package/dist/chunk-FCF57DZI.js.map +1 -0
- package/dist/{chunk-JEOUQFT3.js → chunk-TFCSNRD5.js} +4 -3
- package/dist/chunk-TFCSNRD5.js.map +1 -0
- package/dist/{chunk-AQT3BQ67.js → chunk-Y3KINNAU.js} +12 -5
- package/dist/chunk-Y3KINNAU.js.map +1 -0
- package/dist/{create-config-D5WqfUft.d.ts → create-config-CgYQDyMD.d.ts} +2 -2
- package/dist/index.css +31 -17
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{marketplace-config-C_fDWzz0.d.ts → marketplace-config-BP5-XnFG.d.ts} +1 -1
- package/dist/{marketplace.gen-B8S8fflj.d.ts → marketplace.gen-BzmWLP9L.d.ts} +14 -4
- package/dist/react/_internal/api/index.d.ts +2 -2
- package/dist/react/_internal/api/index.js +3 -1
- package/dist/react/_internal/index.d.ts +5 -5
- package/dist/react/_internal/index.js +4 -2
- package/dist/react/_internal/wagmi/index.d.ts +3 -3
- package/dist/react/_internal/wagmi/index.js +1 -1
- package/dist/react/hooks/index.d.ts +46 -5
- package/dist/react/hooks/index.js +12 -5
- package/dist/react/index.css +37 -17
- package/dist/react/index.css.map +1 -1
- package/dist/react/index.d.ts +6 -6
- package/dist/react/index.js +15 -8
- package/dist/react/ssr/index.js +6 -1
- package/dist/react/ssr/index.js.map +1 -1
- package/dist/react/ui/components/collectible-card/index.css +37 -17
- package/dist/react/ui/components/collectible-card/index.css.map +1 -1
- package/dist/react/ui/components/collectible-card/index.d.ts +27 -4
- package/dist/react/ui/components/collectible-card/index.js +7 -8
- package/dist/react/ui/index.css +37 -17
- package/dist/react/ui/index.css.map +1 -1
- package/dist/react/ui/index.d.ts +3 -3
- package/dist/react/ui/index.js +7 -8
- package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +3 -3
- package/dist/react/ui/modals/_internal/components/actionModal/index.js +5 -5
- package/dist/{sdk-config-BXVH8PS2.d.ts → sdk-config-LbbmA85k.d.ts} +27 -7
- package/dist/{services-CdXAIjt1.d.ts → services-DW26ougH.d.ts} +1 -1
- package/dist/styles/index.css +31 -17
- package/dist/styles/index.css.map +1 -1
- package/dist/styles/index.d.ts +2 -2
- package/dist/styles/index.js +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +2 -2
- package/dist/{types-eX4P9xju.d.ts → types-DhTZWw1U.d.ts} +2 -2
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -3
- package/package.json +3 -2
- package/src/react/__tests__/provider.test.tsx +75 -0
- package/src/react/_internal/api/__mocks__/marketplace.msw.ts +4 -0
- package/src/react/_internal/api/marketplace.gen.ts +42 -7
- package/src/react/_internal/wagmi/__tests__/create-config.test.ts +196 -0
- package/src/react/_internal/wagmi/create-config.ts +9 -1
- package/src/react/_internal/wallet/useWallet.ts +30 -46
- package/src/react/_internal/wallet/wallet.ts +52 -6
- package/src/react/hooks/index.ts +2 -0
- package/src/react/hooks/options/__mocks__/marketplaceConfig.msw.ts +10 -1
- package/src/react/hooks/useCollectionDetails.tsx +35 -0
- package/src/react/hooks/useCollectionDetailsPolling.tsx +60 -0
- package/src/react/provider.tsx +5 -0
- package/src/react/ui/components/_internals/action-button/ActionButton.tsx +36 -118
- package/src/react/ui/components/_internals/action-button/components/ActionButtonBody.tsx +52 -0
- package/src/react/ui/components/_internals/action-button/components/NonOwnerActions.tsx +72 -0
- package/src/react/ui/components/_internals/action-button/components/OwnerActions.tsx +81 -0
- package/src/react/ui/components/_internals/action-button/hooks/useActionButtonLogic.ts +93 -0
- package/src/react/ui/components/_internals/action-button/store.ts +47 -0
- package/src/react/ui/components/_internals/action-button/styles.css.ts +8 -0
- package/src/react/ui/components/collectible-card/CollectibleCard.tsx +34 -14
- package/src/react/ui/components/collectible-card/Footer.tsx +9 -9
- package/src/react/ui/components/collectible-card/styles.css.ts +44 -31
- package/src/react/ui/icons/CartIcon.tsx +46 -0
- package/src/react/ui/modals/BuyModal/Modal.tsx +11 -4
- package/src/react/ui/modals/BuyModal/__tests__/Modal.test.tsx +253 -0
- package/src/react/ui/modals/BuyModal/__tests__/store.test.ts +100 -0
- package/src/react/ui/modals/BuyModal/hooks/__tests__/useBuyCollectable.test.tsx +402 -0
- package/src/react/ui/modals/BuyModal/hooks/__tests__/useCheckoutOptions.test.tsx +267 -0
- package/src/react/ui/modals/BuyModal/hooks/__tests__/useFees.test.tsx +166 -0
- package/src/react/ui/modals/BuyModal/hooks/__tests__/useLoadData.test.tsx +209 -0
- package/src/react/ui/modals/BuyModal/hooks/useCheckoutOptions.ts +19 -17
- package/src/react/ui/modals/BuyModal/hooks/useLoadData.ts +9 -7
- package/src/react/ui/modals/BuyModal/modals/CheckoutModal.tsx +7 -0
- package/src/react/ui/modals/BuyModal/modals/Modal1155.tsx +36 -18
- package/src/react/ui/modals/BuyModal/modals/__tests__/CheckoutModal.test.tsx +162 -0
- package/src/react/ui/modals/BuyModal/modals/__tests__/Modal1155.test.tsx +243 -0
- package/src/react/ui/modals/BuyModal/store.ts +11 -10
- package/src/react/ui/modals/CreateListingModal/Modal.tsx +26 -3
- package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +141 -29
- package/src/react/ui/modals/SuccessfulPurchaseModal/__tests__/Modal.test.tsx +145 -0
- package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +20 -11
- package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx +13 -58
- package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +2 -0
- package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/TransactionStatusModal.test.tsx +18 -19
- package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/utils.test.ts +2 -0
- package/src/react/ui/modals/_internal/components/transactionStatusModal/hooks/useTransactionStatus.ts +62 -0
- package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +53 -100
- package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +2 -10
- package/src/utils/_internal/error/config.ts +16 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/chunk-5UCKYAMR.js.map +0 -1
- package/dist/chunk-6R4G7J6Q.js.map +0 -1
- package/dist/chunk-AQT3BQ67.js.map +0 -1
- package/dist/chunk-FWN2MCLI.js +0 -425
- package/dist/chunk-FWN2MCLI.js.map +0 -1
- package/dist/chunk-JEOUQFT3.js.map +0 -1
- package/dist/chunk-R7GVMKMM.js.map +0 -1
- package/dist/chunk-ZEH4JI2U.js.map +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { useCheckoutOptions } from '../useCheckoutOptions';
|
|
4
|
+
import { useWallet } from '../../../../../_internal/wallet/useWallet';
|
|
5
|
+
import { useConfig } from '../../../../../hooks';
|
|
6
|
+
import { useFees } from '../useFees';
|
|
7
|
+
import {
|
|
8
|
+
MarketplaceKind,
|
|
9
|
+
getMarketplaceClient,
|
|
10
|
+
} from '../../../../../_internal';
|
|
11
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
12
|
+
import type { ReactNode } from 'react';
|
|
13
|
+
|
|
14
|
+
// Mock dependencies
|
|
15
|
+
vi.mock('../../../../../_internal/wallet/useWallet', () => ({
|
|
16
|
+
useWallet: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock('../../../../../hooks', () => ({
|
|
20
|
+
useConfig: vi.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
vi.mock('../useFees', () => ({
|
|
24
|
+
useFees: vi.fn(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock('../../../../../_internal', async () => {
|
|
28
|
+
const actual = (await vi.importActual('../../../../../_internal')) as Record<
|
|
29
|
+
string,
|
|
30
|
+
unknown
|
|
31
|
+
>;
|
|
32
|
+
return {
|
|
33
|
+
...actual,
|
|
34
|
+
getMarketplaceClient: vi.fn(),
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const createWrapper = () => {
|
|
39
|
+
const queryClient = new QueryClient({
|
|
40
|
+
defaultOptions: {
|
|
41
|
+
queries: {
|
|
42
|
+
retry: false,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
return ({ children }: { children: ReactNode }) => (
|
|
47
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe('useCheckoutOptions', () => {
|
|
52
|
+
const defaultProps = {
|
|
53
|
+
chainId: 1,
|
|
54
|
+
collectionAddress: '0x123' as `0x${string}`,
|
|
55
|
+
orderId: '1',
|
|
56
|
+
marketplace: MarketplaceKind.sequence_marketplace_v2,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
vi.clearAllMocks();
|
|
61
|
+
|
|
62
|
+
// Setup default mock implementations
|
|
63
|
+
(useConfig as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
64
|
+
projectAccessKey: 'test-key',
|
|
65
|
+
projectId: 'test-id',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
(useFees as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
69
|
+
amount: '250',
|
|
70
|
+
receiver: '0x123',
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should return undefined when wallet is not available', () => {
|
|
75
|
+
// Mock useWallet to return no wallet
|
|
76
|
+
(useWallet as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
77
|
+
wallet: undefined,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const { result } = renderHook(() => useCheckoutOptions(defaultProps), {
|
|
81
|
+
wrapper: createWrapper(),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(result.current.data).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should fetch checkout options when wallet is available', async () => {
|
|
88
|
+
// Mock useWallet to return a valid wallet
|
|
89
|
+
(useWallet as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
90
|
+
wallet: {
|
|
91
|
+
address: async () => '0x123',
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Mock the marketplace client
|
|
96
|
+
const checkoutOptionsMarketplaceMock = vi.fn().mockResolvedValue({
|
|
97
|
+
options: {
|
|
98
|
+
swap: [],
|
|
99
|
+
nftCheckout: [],
|
|
100
|
+
onRamp: [],
|
|
101
|
+
crypto: ['ETH', 'USDC'],
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const marketplaceClientMock = {
|
|
106
|
+
checkoutOptionsMarketplace: checkoutOptionsMarketplaceMock,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
(
|
|
110
|
+
getMarketplaceClient as unknown as ReturnType<typeof vi.fn>
|
|
111
|
+
).mockReturnValue(marketplaceClientMock);
|
|
112
|
+
|
|
113
|
+
const { result } = renderHook(() => useCheckoutOptions(defaultProps), {
|
|
114
|
+
wrapper: createWrapper(),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Wait for the query to complete
|
|
118
|
+
await vi.waitFor(() => {
|
|
119
|
+
expect(result.current.isSuccess).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Verify the marketplace client was called with correct parameters
|
|
123
|
+
expect(checkoutOptionsMarketplaceMock).toHaveBeenCalledWith({
|
|
124
|
+
wallet: '0x123',
|
|
125
|
+
orders: [
|
|
126
|
+
{
|
|
127
|
+
contractAddress: defaultProps.collectionAddress,
|
|
128
|
+
orderId: defaultProps.orderId,
|
|
129
|
+
marketplace: defaultProps.marketplace,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
additionalFee: 250,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Verify the returned data
|
|
136
|
+
expect(result.current.data).toEqual({
|
|
137
|
+
swap: [],
|
|
138
|
+
nftCheckout: [],
|
|
139
|
+
onRamp: [],
|
|
140
|
+
crypto: ['ETH', 'USDC'],
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should call marketplaceClient.checkoutOptionsMarketplace with correct params', async () => {
|
|
145
|
+
// Mock useWallet to return a valid wallet
|
|
146
|
+
(useWallet as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
147
|
+
wallet: {
|
|
148
|
+
address: async () => '0x456',
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Mock the marketplace client
|
|
153
|
+
const checkoutOptionsMarketplaceMock = vi.fn().mockResolvedValue({
|
|
154
|
+
options: {
|
|
155
|
+
swap: ['uniswap'],
|
|
156
|
+
nftCheckout: ['moonpay'],
|
|
157
|
+
onRamp: ['ramp'],
|
|
158
|
+
crypto: ['ETH'],
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const marketplaceClientMock = {
|
|
163
|
+
checkoutOptionsMarketplace: checkoutOptionsMarketplaceMock,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
(
|
|
167
|
+
getMarketplaceClient as unknown as ReturnType<typeof vi.fn>
|
|
168
|
+
).mockReturnValue(marketplaceClientMock);
|
|
169
|
+
|
|
170
|
+
const customProps = {
|
|
171
|
+
chainId: 137,
|
|
172
|
+
collectionAddress: '0x789' as `0x${string}`,
|
|
173
|
+
orderId: '42',
|
|
174
|
+
marketplace: MarketplaceKind.sequence_marketplace_v2,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// Mock fees with different values to ensure they're passed correctly
|
|
178
|
+
(useFees as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
179
|
+
amount: '500',
|
|
180
|
+
receiver: '0xabc',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
renderHook(() => useCheckoutOptions(customProps), {
|
|
184
|
+
wrapper: createWrapper(),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Wait for the query to be called
|
|
188
|
+
await vi.waitFor(() => {
|
|
189
|
+
expect(checkoutOptionsMarketplaceMock).toHaveBeenCalledTimes(1);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Verify exact parameters passed to the marketplace client
|
|
193
|
+
expect(checkoutOptionsMarketplaceMock).toHaveBeenCalledWith({
|
|
194
|
+
wallet: '0x456',
|
|
195
|
+
orders: [
|
|
196
|
+
{
|
|
197
|
+
contractAddress: customProps.collectionAddress,
|
|
198
|
+
orderId: customProps.orderId,
|
|
199
|
+
marketplace: customProps.marketplace,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
additionalFee: 500, // Using the mocked fee amount
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Verify the marketplace client was initialized with correct chain ID
|
|
206
|
+
expect(getMarketplaceClient).toHaveBeenCalledWith(
|
|
207
|
+
customProps.chainId,
|
|
208
|
+
expect.objectContaining({
|
|
209
|
+
projectAccessKey: 'test-key',
|
|
210
|
+
projectId: 'test-id',
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should handle API errors', async () => {
|
|
216
|
+
// Mock useWallet to return a valid wallet
|
|
217
|
+
(useWallet as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
218
|
+
wallet: {
|
|
219
|
+
address: async () => '0x123',
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Mock the marketplace client to throw an error
|
|
224
|
+
const errorMessage = 'Failed to fetch checkout options';
|
|
225
|
+
const checkoutOptionsMarketplaceMock = vi
|
|
226
|
+
.fn()
|
|
227
|
+
.mockRejectedValue(new Error(errorMessage));
|
|
228
|
+
|
|
229
|
+
const marketplaceClientMock = {
|
|
230
|
+
checkoutOptionsMarketplace: checkoutOptionsMarketplaceMock,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
(
|
|
234
|
+
getMarketplaceClient as unknown as ReturnType<typeof vi.fn>
|
|
235
|
+
).mockReturnValue(marketplaceClientMock);
|
|
236
|
+
|
|
237
|
+
const { result } = renderHook(() => useCheckoutOptions(defaultProps), {
|
|
238
|
+
wrapper: createWrapper(),
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Wait for the query to fail
|
|
242
|
+
await vi.waitFor(() => {
|
|
243
|
+
expect(result.current.isError).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Verify error state
|
|
247
|
+
expect(result.current.error).toBeDefined();
|
|
248
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
249
|
+
expect((result.current.error as Error).message).toBe(errorMessage);
|
|
250
|
+
|
|
251
|
+
// Verify data is undefined when there's an error
|
|
252
|
+
expect(result.current.data).toBeUndefined();
|
|
253
|
+
|
|
254
|
+
// Verify the API was called with correct parameters despite the error
|
|
255
|
+
expect(checkoutOptionsMarketplaceMock).toHaveBeenCalledWith({
|
|
256
|
+
wallet: '0x123',
|
|
257
|
+
orders: [
|
|
258
|
+
{
|
|
259
|
+
contractAddress: defaultProps.collectionAddress,
|
|
260
|
+
orderId: defaultProps.orderId,
|
|
261
|
+
marketplace: defaultProps.marketplace,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
additionalFee: 250,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { useFees } from '../useFees';
|
|
4
|
+
import { useMarketplaceConfig } from '../../../../../hooks';
|
|
5
|
+
import { avalanche, optimism } from 'viem/chains';
|
|
6
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
7
|
+
import type { ReactNode } from 'react';
|
|
8
|
+
|
|
9
|
+
// Mock dependencies
|
|
10
|
+
vi.mock('../../../../../hooks', () => ({
|
|
11
|
+
useMarketplaceConfig: vi.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const createWrapper = () => {
|
|
15
|
+
const queryClient = new QueryClient({
|
|
16
|
+
defaultOptions: {
|
|
17
|
+
queries: {
|
|
18
|
+
retry: false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return ({ children }: { children: ReactNode }) => (
|
|
23
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('useFees', () => {
|
|
28
|
+
const defaultProps = {
|
|
29
|
+
chainId: 1,
|
|
30
|
+
collectionAddress: '0x123',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const defaultPlatformFeeRecipient =
|
|
34
|
+
'0x858dB1cbF6D09D447C96A11603189b49B2D1C219';
|
|
35
|
+
const avalancheAndOptimismPlatformFeeRecipient =
|
|
36
|
+
'0x400cdab4676c17aec07e8ec748a5fc3b674bca41';
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
vi.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return default fee when collection not found', () => {
|
|
43
|
+
// Mock marketplace config with no matching collection
|
|
44
|
+
(
|
|
45
|
+
useMarketplaceConfig as unknown as ReturnType<typeof vi.fn>
|
|
46
|
+
).mockReturnValue({
|
|
47
|
+
data: {
|
|
48
|
+
collections: [
|
|
49
|
+
{
|
|
50
|
+
collectionAddress: '0x456',
|
|
51
|
+
chainId: '1',
|
|
52
|
+
marketplaceFeePercentage: '5.0',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const { result } = renderHook(() => useFees(defaultProps), {
|
|
59
|
+
wrapper: createWrapper(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Default fee should be 2.5%, which is 250 in BPS (basis points)
|
|
63
|
+
expect(result.current).toEqual({
|
|
64
|
+
amount: '250',
|
|
65
|
+
receiver: defaultPlatformFeeRecipient,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should return collection-specific fee when found in config', () => {
|
|
70
|
+
const collectionFee = '5.0';
|
|
71
|
+
const collectionAddress = '0x789';
|
|
72
|
+
const chainId = 1;
|
|
73
|
+
|
|
74
|
+
// Mock marketplace config with a matching collection
|
|
75
|
+
(
|
|
76
|
+
useMarketplaceConfig as unknown as ReturnType<typeof vi.fn>
|
|
77
|
+
).mockReturnValue({
|
|
78
|
+
data: {
|
|
79
|
+
collections: [
|
|
80
|
+
{
|
|
81
|
+
collectionAddress,
|
|
82
|
+
chainId: chainId.toString(),
|
|
83
|
+
marketplaceFeePercentage: collectionFee,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const { result } = renderHook(
|
|
90
|
+
() => useFees({ chainId, collectionAddress }),
|
|
91
|
+
{
|
|
92
|
+
wrapper: createWrapper(),
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// 5.0% fee should be 500 in BPS (basis points)
|
|
97
|
+
expect(result.current).toEqual({
|
|
98
|
+
amount: '500',
|
|
99
|
+
receiver: defaultPlatformFeeRecipient,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should use Avalanche/Optimism fee recipient for those chains', () => {
|
|
104
|
+
// Test Avalanche chain
|
|
105
|
+
const { result: avalancheResult } = renderHook(
|
|
106
|
+
() => useFees({ ...defaultProps, chainId: avalanche.id }),
|
|
107
|
+
{
|
|
108
|
+
wrapper: createWrapper(),
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
expect(avalancheResult.current.receiver).toBe(
|
|
113
|
+
avalancheAndOptimismPlatformFeeRecipient,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Test Optimism chain
|
|
117
|
+
const { result: optimismResult } = renderHook(
|
|
118
|
+
() => useFees({ ...defaultProps, chainId: optimism.id }),
|
|
119
|
+
{
|
|
120
|
+
wrapper: createWrapper(),
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(optimismResult.current.receiver).toBe(
|
|
125
|
+
avalancheAndOptimismPlatformFeeRecipient,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should handle case-insensitive collection address matching', () => {
|
|
130
|
+
const collectionFee = '3.5';
|
|
131
|
+
const collectionAddress = '0xABC123';
|
|
132
|
+
const chainId = 1;
|
|
133
|
+
|
|
134
|
+
// Mock marketplace config with a collection in different case
|
|
135
|
+
(
|
|
136
|
+
useMarketplaceConfig as unknown as ReturnType<typeof vi.fn>
|
|
137
|
+
).mockReturnValue({
|
|
138
|
+
data: {
|
|
139
|
+
collections: [
|
|
140
|
+
{
|
|
141
|
+
collectionAddress: collectionAddress.toLowerCase(),
|
|
142
|
+
chainId: chainId.toString(),
|
|
143
|
+
marketplaceFeePercentage: collectionFee,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const { result } = renderHook(
|
|
150
|
+
() =>
|
|
151
|
+
useFees({
|
|
152
|
+
chainId,
|
|
153
|
+
collectionAddress: collectionAddress.toUpperCase(),
|
|
154
|
+
}),
|
|
155
|
+
{
|
|
156
|
+
wrapper: createWrapper(),
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// 3.5% fee should be 350 in BPS (basis points)
|
|
161
|
+
expect(result.current).toEqual({
|
|
162
|
+
amount: '350',
|
|
163
|
+
receiver: defaultPlatformFeeRecipient,
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { useLoadData } from '../useLoadData';
|
|
4
|
+
import { useCollection, useCollectible } from '../../../../../hooks';
|
|
5
|
+
import { useCheckoutOptions } from '../useCheckoutOptions';
|
|
6
|
+
import { MarketplaceKind } from '../../../../../_internal';
|
|
7
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
|
|
10
|
+
// Mock dependencies
|
|
11
|
+
vi.mock('../../../../../hooks', () => ({
|
|
12
|
+
useCollection: vi.fn(),
|
|
13
|
+
useCollectible: vi.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock('../useCheckoutOptions', () => ({
|
|
17
|
+
useCheckoutOptions: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const createWrapper = () => {
|
|
21
|
+
const queryClient = new QueryClient({
|
|
22
|
+
defaultOptions: {
|
|
23
|
+
queries: {
|
|
24
|
+
retry: false,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return ({ children }: { children: ReactNode }) => (
|
|
29
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('useLoadData', () => {
|
|
34
|
+
const defaultProps = {
|
|
35
|
+
chainId: 1,
|
|
36
|
+
collectionAddress: '0x123' as `0x${string}`,
|
|
37
|
+
collectibleId: '1',
|
|
38
|
+
orderId: '1',
|
|
39
|
+
marketplace: MarketplaceKind.sequence_marketplace_v2,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
vi.clearAllMocks();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return loading state when any data is loading', () => {
|
|
47
|
+
// Mock one hook to be loading
|
|
48
|
+
(useCollection as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
49
|
+
data: null,
|
|
50
|
+
isLoading: true,
|
|
51
|
+
isError: false,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
(useCollectible as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
55
|
+
data: null,
|
|
56
|
+
isLoading: false,
|
|
57
|
+
isError: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
(useCheckoutOptions as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
|
|
61
|
+
{
|
|
62
|
+
data: null,
|
|
63
|
+
isLoading: false,
|
|
64
|
+
isError: false,
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const { result } = renderHook(() => useLoadData(defaultProps), {
|
|
69
|
+
wrapper: createWrapper(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(result.current.isLoading).toBe(true);
|
|
73
|
+
expect(result.current.isError).toBe(false);
|
|
74
|
+
expect(result.current.collection).toBeNull();
|
|
75
|
+
expect(result.current.collectable).toBeNull();
|
|
76
|
+
expect(result.current.checkoutOptions).toBeNull();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should return error state when any request fails', () => {
|
|
80
|
+
// Mock one hook to have an error
|
|
81
|
+
(useCollection as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
82
|
+
data: null,
|
|
83
|
+
isLoading: false,
|
|
84
|
+
isError: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
(useCollectible as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
88
|
+
data: null,
|
|
89
|
+
isLoading: false,
|
|
90
|
+
isError: false,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
(useCheckoutOptions as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
|
|
94
|
+
{
|
|
95
|
+
data: null,
|
|
96
|
+
isLoading: false,
|
|
97
|
+
isError: false,
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const { result } = renderHook(() => useLoadData(defaultProps), {
|
|
102
|
+
wrapper: createWrapper(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(result.current.isError).toBe(true);
|
|
106
|
+
expect(result.current.isLoading).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should return all data when successfully loaded', () => {
|
|
110
|
+
const mockCollection = { type: 'ERC721', name: 'Test Collection' };
|
|
111
|
+
const mockCollectable = { tokenId: '1', name: 'Test NFT' };
|
|
112
|
+
const mockCheckoutOptions = {
|
|
113
|
+
swap: [],
|
|
114
|
+
nftCheckout: [],
|
|
115
|
+
onRamp: [],
|
|
116
|
+
crypto: ['ETH'],
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
(useCollection as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
120
|
+
data: mockCollection,
|
|
121
|
+
isLoading: false,
|
|
122
|
+
isError: false,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
(useCollectible as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
126
|
+
data: mockCollectable,
|
|
127
|
+
isLoading: false,
|
|
128
|
+
isError: false,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
(useCheckoutOptions as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
|
|
132
|
+
{
|
|
133
|
+
data: mockCheckoutOptions,
|
|
134
|
+
isLoading: false,
|
|
135
|
+
isError: false,
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const { result } = renderHook(() => useLoadData(defaultProps), {
|
|
140
|
+
wrapper: createWrapper(),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(result.current.isLoading).toBe(false);
|
|
144
|
+
expect(result.current.isError).toBe(false);
|
|
145
|
+
expect(result.current.collection).toBe(mockCollection);
|
|
146
|
+
expect(result.current.collectable).toBe(mockCollectable);
|
|
147
|
+
expect(result.current.checkoutOptions).toBe(mockCheckoutOptions);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should handle partial data loading states', () => {
|
|
151
|
+
// Mock different loading states for each hook
|
|
152
|
+
(useCollection as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
153
|
+
data: { type: 'ERC721', name: 'Test Collection' },
|
|
154
|
+
isLoading: false,
|
|
155
|
+
isError: false,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
(useCollectible as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
159
|
+
data: null,
|
|
160
|
+
isLoading: true,
|
|
161
|
+
isError: false,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
(useCheckoutOptions as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
|
|
165
|
+
{
|
|
166
|
+
data: {
|
|
167
|
+
swap: [],
|
|
168
|
+
nftCheckout: [],
|
|
169
|
+
onRamp: [],
|
|
170
|
+
crypto: ['ETH'],
|
|
171
|
+
},
|
|
172
|
+
isLoading: false,
|
|
173
|
+
isError: false,
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const { result } = renderHook(() => useLoadData(defaultProps), {
|
|
178
|
+
wrapper: createWrapper(),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Should be loading if any data is loading
|
|
182
|
+
expect(result.current.isLoading).toBe(true);
|
|
183
|
+
expect(result.current.isError).toBe(false);
|
|
184
|
+
|
|
185
|
+
// Should have partial data available
|
|
186
|
+
expect(result.current.collection).toBeDefined();
|
|
187
|
+
expect(result.current.collectable).toBeNull();
|
|
188
|
+
expect(result.current.checkoutOptions).toBeDefined();
|
|
189
|
+
|
|
190
|
+
// Verify hook calls
|
|
191
|
+
expect(useCollection).toHaveBeenCalledWith({
|
|
192
|
+
chainId: defaultProps.chainId,
|
|
193
|
+
collectionAddress: defaultProps.collectionAddress,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
expect(useCollectible).toHaveBeenCalledWith({
|
|
197
|
+
chainId: String(defaultProps.chainId),
|
|
198
|
+
collectionAddress: defaultProps.collectionAddress,
|
|
199
|
+
collectibleId: defaultProps.collectibleId,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(useCheckoutOptions).toHaveBeenCalledWith({
|
|
203
|
+
chainId: defaultProps.chainId,
|
|
204
|
+
collectionAddress: defaultProps.collectionAddress,
|
|
205
|
+
orderId: defaultProps.orderId,
|
|
206
|
+
marketplace: defaultProps.marketplace,
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import { useConfig } from '../../../../hooks';
|
|
7
7
|
import { useWallet } from '../../../../_internal/wallet/useWallet';
|
|
8
8
|
import { useFees } from './useFees';
|
|
9
|
-
import { useQuery } from '@tanstack/react-query';
|
|
9
|
+
import { skipToken, useQuery } from '@tanstack/react-query';
|
|
10
10
|
|
|
11
11
|
export const useCheckoutOptions = (input: {
|
|
12
12
|
chainId: number;
|
|
@@ -29,22 +29,24 @@ export const useCheckoutOptions = (input: {
|
|
|
29
29
|
input.orderId,
|
|
30
30
|
input.marketplace,
|
|
31
31
|
],
|
|
32
|
-
queryFn:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
queryFn: wallet
|
|
33
|
+
? async () => {
|
|
34
|
+
const marketplaceClient = getMarketplaceClient(input.chainId, config);
|
|
35
|
+
return marketplaceClient
|
|
36
|
+
.checkoutOptionsMarketplace({
|
|
37
|
+
wallet: await wallet.address(),
|
|
38
|
+
orders: [
|
|
39
|
+
{
|
|
40
|
+
contractAddress: input.collectionAddress,
|
|
41
|
+
orderId: input.orderId,
|
|
42
|
+
marketplace: input.marketplace,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
additionalFee: Number(fees.amount),
|
|
46
|
+
})
|
|
47
|
+
.then((res) => res.options);
|
|
48
|
+
}
|
|
49
|
+
: skipToken,
|
|
48
50
|
enabled: !!wallet,
|
|
49
51
|
});
|
|
50
52
|
};
|