@0xsequence/marketplace-sdk 0.4.9 → 0.5.1

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.
Files changed (77) hide show
  1. package/dist/{chunk-BMWCIHCB.js → chunk-5NORRVPM.js} +1 -1
  2. package/dist/{chunk-BMWCIHCB.js.map → chunk-5NORRVPM.js.map} +1 -1
  3. package/dist/{chunk-ATDCYXXV.js → chunk-6YHHCGGY.js} +2 -2
  4. package/dist/{chunk-BJLOO4NP.js → chunk-F4E3WJ2K.js} +30 -26
  5. package/dist/chunk-F4E3WJ2K.js.map +1 -0
  6. package/dist/{chunk-CUA4SGWT.js → chunk-I37CRQ4S.js} +750 -561
  7. package/dist/chunk-I37CRQ4S.js.map +1 -0
  8. package/dist/{chunk-TFCSNRD5.js → chunk-LJAB3S6U.js} +1 -1
  9. package/dist/chunk-LJAB3S6U.js.map +1 -0
  10. package/dist/{chunk-Y3KINNAU.js → chunk-MKGSGTQC.js} +14 -7
  11. package/dist/chunk-MKGSGTQC.js.map +1 -0
  12. package/dist/{chunk-XXML5K3X.js → chunk-QTJF5GDQ.js} +2 -2
  13. package/dist/{chunk-LF44FCG5.js → chunk-TQWM4ER6.js} +2 -2
  14. package/dist/{chunk-LF44FCG5.js.map → chunk-TQWM4ER6.js.map} +1 -1
  15. package/dist/{chunk-7HWJ4DUX.js → chunk-WSCUPAGR.js} +7 -3
  16. package/dist/chunk-WSCUPAGR.js.map +1 -0
  17. package/dist/{create-config-CgYQDyMD.d.ts → create-config-BXvwUh55.d.ts} +2 -2
  18. package/dist/index.css +2 -2
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +2 -2
  21. package/dist/{marketplace-config-BP5-XnFG.d.ts → marketplace-config-znEu4L0K.d.ts} +1 -1
  22. package/dist/{marketplace.gen-BzmWLP9L.d.ts → marketplace.gen-CCJ-URn2.d.ts} +2 -0
  23. package/dist/react/_internal/api/index.d.ts +3 -2
  24. package/dist/react/_internal/api/index.js +1 -1
  25. package/dist/react/_internal/index.d.ts +5 -5
  26. package/dist/react/_internal/index.js +1 -1
  27. package/dist/react/_internal/wagmi/index.d.ts +3 -3
  28. package/dist/react/hooks/index.d.ts +299 -94
  29. package/dist/react/hooks/index.js +11 -5
  30. package/dist/react/index.css +2 -2
  31. package/dist/react/index.css.map +1 -1
  32. package/dist/react/index.d.ts +6 -6
  33. package/dist/react/index.js +15 -9
  34. package/dist/react/ssr/index.js +4 -0
  35. package/dist/react/ssr/index.js.map +1 -1
  36. package/dist/react/ui/components/collectible-card/index.css +2 -2
  37. package/dist/react/ui/components/collectible-card/index.css.map +1 -1
  38. package/dist/react/ui/components/collectible-card/index.d.ts +3 -3
  39. package/dist/react/ui/components/collectible-card/index.js +9 -9
  40. package/dist/react/ui/icons/index.js +3 -3
  41. package/dist/react/ui/index.css +2 -2
  42. package/dist/react/ui/index.css.map +1 -1
  43. package/dist/react/ui/index.d.ts +3 -3
  44. package/dist/react/ui/index.js +9 -9
  45. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +3 -3
  46. package/dist/react/ui/modals/_internal/components/actionModal/index.js +6 -6
  47. package/dist/{sdk-config-LbbmA85k.d.ts → sdk-config-B32_2bG3.d.ts} +4 -2
  48. package/dist/{services-DW26ougH.d.ts → services-BRBVE0mm.d.ts} +1 -1
  49. package/dist/styles/index.css +2 -2
  50. package/dist/styles/index.css.map +1 -1
  51. package/dist/styles/index.js +2 -2
  52. package/dist/types/index.d.ts +3 -3
  53. package/dist/types/index.js +1 -1
  54. package/dist/{types-DhTZWw1U.d.ts → types-Yto6KrTN.d.ts} +2 -2
  55. package/dist/utils/index.d.ts +3 -3
  56. package/dist/utils/index.js +2 -2
  57. package/package.json +16 -16
  58. package/src/react/_internal/api/marketplace.gen.ts +6 -3
  59. package/src/react/_internal/api/query-keys.ts +4 -0
  60. package/src/react/hooks/index.ts +2 -0
  61. package/src/react/hooks/useAutoSelectFeeOption.tsx +197 -0
  62. package/src/react/hooks/useCancelOrder.tsx +41 -1
  63. package/src/react/hooks/useCollectionBalanceDetails.tsx +87 -0
  64. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +10 -12
  65. package/src/react/ui/components/collectible-card/styles.css.ts +1 -1
  66. package/src/react/ui/icons/CartIcon.tsx +2 -2
  67. package/src/react/ui/modals/BuyModal/hooks/useBuyCollectable.ts +7 -4
  68. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/useHandleTransfer.tsx +5 -1
  69. package/src/react/ui/modals/_internal/components/waasFeeOptionsSelect/WaasFeeOptionsSelect.tsx +14 -7
  70. package/tsconfig.tsbuildinfo +1 -1
  71. package/dist/chunk-7HWJ4DUX.js.map +0 -1
  72. package/dist/chunk-BJLOO4NP.js.map +0 -1
  73. package/dist/chunk-CUA4SGWT.js.map +0 -1
  74. package/dist/chunk-TFCSNRD5.js.map +0 -1
  75. package/dist/chunk-Y3KINNAU.js.map +0 -1
  76. /package/dist/{chunk-ATDCYXXV.js.map → chunk-6YHHCGGY.js.map} +0 -0
  77. /package/dist/{chunk-XXML5K3X.js.map → chunk-QTJF5GDQ.js.map} +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@0xsequence/marketplace-sdk",
3
3
  "private": false,
4
- "version": "0.4.9",
4
+ "version": "0.5.1",
5
5
  "type": "module",
6
6
  "sideEffects": [
7
7
  "**/*.css"
@@ -34,27 +34,27 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@legendapp/state": "^3.0.0-beta.26",
37
- "@radix-ui/react-dialog": "^1.1.5",
38
- "@radix-ui/react-popover": "^1.1.5",
39
- "@radix-ui/react-select": "^2.1.5",
37
+ "@radix-ui/react-dialog": "^1.1.6",
38
+ "@radix-ui/react-popover": "^1.1.6",
39
+ "@radix-ui/react-select": "^2.1.6",
40
40
  "date-fns": "^4.1.0",
41
41
  "react-day-picker": "^9.5.1",
42
42
  "zod": "^3.24.1"
43
43
  },
44
44
  "peerDependencies": {
45
- "0xsequence": "^2.2.8",
46
- "@0xsequence/api": "^2.2.8",
45
+ "0xsequence": "^2.2.13",
46
+ "@0xsequence/api": "^2.2.13",
47
47
  "@0xsequence/design-system": "^1.9.0",
48
- "@0xsequence/indexer": "^2.2.8",
49
- "@0xsequence/kit": "^4.5.9",
50
- "@0xsequence/kit-checkout": "^4.5.9",
51
- "@0xsequence/metadata": "^2.2.8",
52
- "@0xsequence/network": "^2.2.8",
53
- "@tanstack/react-query": "^5.65.1",
48
+ "@0xsequence/indexer": "^2.2.13",
49
+ "@0xsequence/kit": "^4.6.0",
50
+ "@0xsequence/kit-checkout": "^4.6.0",
51
+ "@0xsequence/metadata": "^2.2.13",
52
+ "@0xsequence/network": "^2.2.13",
53
+ "@tanstack/react-query": "^5.66.0",
54
54
  "react": "^18.3.1",
55
55
  "react-dom": "^18.3.1",
56
- "viem": "^2.22.17",
57
- "wagmi": "^2.14.9"
56
+ "viem": "^2.23.2",
57
+ "wagmi": "^2.14.11"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@biomejs/biome": "^1.9.4",
@@ -62,8 +62,8 @@
62
62
  "@testing-library/react": "^16.2.0",
63
63
  "@types/react": "^18.3.18",
64
64
  "@types/react-dom": "^18.3.5",
65
- "@vanilla-extract/css": "^1.17.0",
66
- "@vanilla-extract/esbuild-plugin": "^2.3.13",
65
+ "@vanilla-extract/css": "^1.17.1",
66
+ "@vanilla-extract/esbuild-plugin": "^2.3.15",
67
67
  "@vanilla-extract/recipes": "^0.5.5",
68
68
  "@vanilla-extract/vite-plugin": "^4.0.19",
69
69
  "ctix": "^2.7.0",
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable */
2
- // marketplace-api e84c70e41910cca2160d2377b0554dc58750c67b
2
+ // marketplace-api 8d0cafb3a02c5db5bdd5d6b139a752349e301f0c
3
3
  // --
4
4
  // Code generated by webrpc-gen@v0.22.1 with typescript generator. DO NOT EDIT.
5
5
  //
@@ -7,7 +7,7 @@
7
7
 
8
8
  export const WebrpcHeader = "Webrpc"
9
9
 
10
- export const WebrpcHeaderValue = "webrpc@v0.22.1;gen-typescript@v0.16.2;marketplace-api@v0.0.0-e84c70e41910cca2160d2377b0554dc58750c67b"
10
+ export const WebrpcHeaderValue = "webrpc@v0.22.1;gen-typescript@v0.16.2;marketplace-api@v0.0.0-8d0cafb3a02c5db5bdd5d6b139a752349e301f0c"
11
11
 
12
12
  // WebRPC description and code-gen version
13
13
  export const WebRPCVersion = "v1"
@@ -16,7 +16,7 @@ export const WebRPCVersion = "v1"
16
16
  export const WebRPCSchemaVersion = ""
17
17
 
18
18
  // Schema hash generated from your RIDL schema
19
- export const WebRPCSchemaHash = "e84c70e41910cca2160d2377b0554dc58750c67b"
19
+ export const WebRPCSchemaHash = "8d0cafb3a02c5db5bdd5d6b139a752349e301f0c"
20
20
 
21
21
  type WebrpcGenVersions = {
22
22
  webrpcGenVersion: string;
@@ -350,6 +350,8 @@ export interface FeeBreakdown {
350
350
  export interface CollectibleOrder {
351
351
  metadata: TokenMetadata
352
352
  order?: Order
353
+ listing?: Order
354
+ offer?: Order
353
355
  }
354
356
 
355
357
  export interface OrderFilter {
@@ -2119,3 +2121,4 @@ export const webrpcErrorByCode: { [code: number]: any } = {
2119
2121
  }
2120
2122
 
2121
2123
  export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise<Response>
2124
+
@@ -50,6 +50,10 @@ class CollectionKeys {
50
50
  class BalanceQueries {
51
51
  static all = ['balances'] as const;
52
52
  static lists = [...BalanceQueries.all, 'tokenBalances'] as const;
53
+ static collectionBalanceDetails = [
54
+ ...BalanceQueries.all,
55
+ 'collectionBalanceDetails',
56
+ ] as const;
53
57
  }
54
58
 
55
59
  // biome-ignore lint/complexity/noStaticOnlyClass:
@@ -1,7 +1,9 @@
1
+ export * from './useAutoSelectFeeOption';
1
2
  export * from './useBalanceOfCollectible';
2
3
  export * from './useCountOfCollectables';
3
4
  export * from './useCollectible';
4
5
  export * from './useCollection';
6
+ export * from './useCollectionBalanceDetails';
5
7
  export * from './useConfig';
6
8
  export * from './useCurrencies';
7
9
  export * from './useCurrencyOptions';
@@ -0,0 +1,197 @@
1
+ import { zeroAddress, type Address } from 'viem';
2
+ import { useAccount } from 'wagmi';
3
+ import type { FeeOption } from '../ui/modals/_internal/components/waasFeeOptionsSelect/WaasFeeOptionsSelect';
4
+ import { useCallback } from 'react';
5
+ import { useChain } from '@0xsequence/kit';
6
+ import { useCollectionBalanceDetails } from './useCollectionBalanceDetails';
7
+
8
+ enum AutoSelectFeeOptionError {
9
+ UserNotConnected = 'User not connected',
10
+ NoOptionsProvided = 'No options provided',
11
+ FailedToCheckBalances = 'Failed to check balances',
12
+ InsufficientBalanceForAnyFeeOption = 'Insufficient balance for any fee option',
13
+ }
14
+
15
+ type UseAutoSelectFeeOptionArgs = {
16
+ pendingFeeOptionConfirmation: {
17
+ id: string;
18
+ options: FeeOption[] | undefined;
19
+ chainId: number;
20
+ };
21
+ };
22
+
23
+ /**
24
+ * A React hook that automatically selects the first fee option for which the user has sufficient balance.
25
+ *
26
+ * @param {Object} params.pendingFeeOptionConfirmation - Configuration for fee option selection
27
+ *
28
+ * @returns {Promise<{
29
+ * selectedOption: FeeOption | null,
30
+ * error: AutoSelectFeeOptionError | null,
31
+ * isLoading?: boolean
32
+ * }>} A promise that resolves to an object containing:
33
+ * - selectedOption: The first fee option with sufficient balance, or null if none found
34
+ * - error: Error message if selection fails, null otherwise
35
+ * - isLoading: True while checking balances
36
+ *
37
+ * @throws {AutoSelectFeeOptionError} Possible errors:
38
+ * - UserNotConnected: When no wallet is connected
39
+ * - NoOptionsProvided: When fee options array is undefined
40
+ * - FailedToCheckBalances: When balance checking fails
41
+ * - InsufficientBalanceForAnyFeeOption: When user has insufficient balance for all options
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * function MyComponent() {
46
+ * const [pendingFeeOptionConfirmation, confirmPendingFeeOption] = useWaasFeeOptions();
47
+ *
48
+ * const autoSelectOptionPromise = useAutoSelectFeeOption({
49
+ * pendingFeeOptionConfirmation: pendingFeeOptionConfirmation
50
+ * ? {
51
+ * id: pendingFeeOptionConfirmation.id,
52
+ * options: pendingFeeOptionConfirmation.options,
53
+ * chainId: 1
54
+ * }
55
+ * : {
56
+ * id: '',
57
+ * options: undefined,
58
+ * chainId: 1
59
+ * }
60
+ * });
61
+ *
62
+ * useEffect(() => {
63
+ * autoSelectOptionPromise.then((result) => {
64
+ * if (result.isLoading) {
65
+ * console.log('Checking balances...');
66
+ * return;
67
+ * }
68
+ *
69
+ * if (result.error) {
70
+ * console.error('Failed to select fee option:', result.error);
71
+ * return;
72
+ * }
73
+ *
74
+ * if (pendingFeeOptionConfirmation?.id && result.selectedOption) {
75
+ * confirmPendingFeeOption(
76
+ * pendingFeeOptionConfirmation.id,
77
+ * result.selectedOption.token.contractAddress
78
+ * );
79
+ * }
80
+ * });
81
+ * }, [autoSelectOptionPromise, confirmPendingFeeOption, pendingFeeOptionConfirmation]);
82
+ *
83
+ * return <div>...</div>;
84
+ * }
85
+ * ```
86
+ */
87
+ export function useAutoSelectFeeOption({
88
+ pendingFeeOptionConfirmation,
89
+ }: UseAutoSelectFeeOptionArgs) {
90
+ const { address: userAddress } = useAccount();
91
+
92
+ // one token that has null contract address is native token, so we need to replace it with zero address
93
+ const contractWhitelist = pendingFeeOptionConfirmation.options?.map(
94
+ (option) =>
95
+ option.token.contractAddress === null
96
+ ? zeroAddress
97
+ : (option.token.contractAddress as Address),
98
+ );
99
+
100
+ const {
101
+ data: balanceDetails,
102
+ isLoading: isBalanceDetailsLoading,
103
+ isError: isBalanceDetailsError,
104
+ } = useCollectionBalanceDetails({
105
+ chainId: pendingFeeOptionConfirmation.chainId,
106
+ filter: {
107
+ accountAddresses: userAddress ? [userAddress] : [],
108
+ contractWhitelist,
109
+ omitNativeBalances: false,
110
+ },
111
+ query: {
112
+ enabled: !!pendingFeeOptionConfirmation.options && !!userAddress,
113
+ },
114
+ });
115
+ const chain = useChain(pendingFeeOptionConfirmation.chainId);
116
+
117
+ // combine native balance and erc20 balances
118
+ const combinedBalances = balanceDetails && [
119
+ ...balanceDetails.nativeBalances.map((b) => ({
120
+ chainId: pendingFeeOptionConfirmation.chainId,
121
+ balance: b.balance,
122
+ symbol: chain?.nativeCurrency.symbol,
123
+ contractAddress: zeroAddress,
124
+ })),
125
+ ...balanceDetails.balances.map((b) => ({
126
+ chainId: b.chainId,
127
+ balance: b.balance,
128
+ symbol: b.contractInfo?.symbol,
129
+ contractAddress: b.contractAddress,
130
+ })),
131
+ ];
132
+
133
+ console.debug('currency balances', combinedBalances);
134
+
135
+ const autoSelectedOption = useCallback(async () => {
136
+ if (!userAddress) {
137
+ return {
138
+ selectedOption: null,
139
+ error: AutoSelectFeeOptionError.UserNotConnected,
140
+ };
141
+ }
142
+
143
+ if (!pendingFeeOptionConfirmation.options) {
144
+ return {
145
+ selectedOption: null,
146
+ error: AutoSelectFeeOptionError.NoOptionsProvided,
147
+ };
148
+ }
149
+
150
+ if (isBalanceDetailsLoading) {
151
+ return { selectedOption: null, error: null, isLoading: true };
152
+ }
153
+
154
+ if (isBalanceDetailsError || !combinedBalances) {
155
+ return {
156
+ selectedOption: null,
157
+ error: AutoSelectFeeOptionError.FailedToCheckBalances,
158
+ };
159
+ }
160
+
161
+ const selectedOption = pendingFeeOptionConfirmation.options.find(
162
+ (option) => {
163
+ const tokenBalance = combinedBalances.find(
164
+ (balance) =>
165
+ balance.contractAddress.toLowerCase() ===
166
+ (option.token.contractAddress === null
167
+ ? zeroAddress
168
+ : option.token.contractAddress
169
+ ).toLowerCase(),
170
+ );
171
+
172
+ if (!tokenBalance) return false;
173
+
174
+ return BigInt(tokenBalance.balance) >= BigInt(option.value);
175
+ },
176
+ );
177
+
178
+ if (!selectedOption) {
179
+ return {
180
+ selectedOption: null,
181
+ error: AutoSelectFeeOptionError.InsufficientBalanceForAnyFeeOption,
182
+ };
183
+ }
184
+
185
+ console.debug('auto selected option', selectedOption);
186
+
187
+ return { selectedOption, error: null };
188
+ }, [
189
+ userAddress,
190
+ pendingFeeOptionConfirmation.options,
191
+ isBalanceDetailsLoading,
192
+ isBalanceDetailsError,
193
+ combinedBalances,
194
+ ]);
195
+
196
+ return autoSelectedOption();
197
+ }
@@ -1,6 +1,8 @@
1
- import { useState } from 'react';
1
+ import { useState, useEffect } from 'react';
2
2
  import { useCancelTransactionSteps } from './useCancelTransactionSteps';
3
3
  import type { MarketplaceKind } from '../../types';
4
+ import { useWaasFeeOptions } from '@0xsequence/kit';
5
+ import { useAutoSelectFeeOption } from './useAutoSelectFeeOption';
4
6
 
5
7
  interface UseCancelOrderArgs {
6
8
  collectionAddress: string;
@@ -29,6 +31,44 @@ export const useCancelOrder = ({
29
31
  const [cancellingOrderId, setCancellingOrderId] = useState<string | null>(
30
32
  null,
31
33
  );
34
+ const [pendingFeeOptionConfirmation, confirmPendingFeeOption] =
35
+ useWaasFeeOptions();
36
+ const autoSelectOptionPromise = useAutoSelectFeeOption({
37
+ pendingFeeOptionConfirmation: pendingFeeOptionConfirmation
38
+ ? {
39
+ id: pendingFeeOptionConfirmation.id,
40
+ options: pendingFeeOptionConfirmation.options?.map((opt) => ({
41
+ ...opt,
42
+ token: {
43
+ ...opt.token,
44
+ contractAddress: opt.token.contractAddress || null,
45
+ decimals: opt.token.decimals || 0,
46
+ tokenID: opt.token.tokenID || null,
47
+ },
48
+ })),
49
+ chainId: Number(chainId),
50
+ }
51
+ : {
52
+ id: '',
53
+ options: undefined,
54
+ chainId: Number(chainId),
55
+ },
56
+ });
57
+
58
+ useEffect(() => {
59
+ autoSelectOptionPromise.then((res) => {
60
+ if (pendingFeeOptionConfirmation?.id && res.selectedOption) {
61
+ confirmPendingFeeOption(
62
+ pendingFeeOptionConfirmation.id,
63
+ res.selectedOption.token.contractAddress,
64
+ );
65
+ }
66
+ });
67
+ }, [
68
+ autoSelectOptionPromise,
69
+ confirmPendingFeeOption,
70
+ pendingFeeOptionConfirmation,
71
+ ]);
32
72
 
33
73
  const { cancelOrder: cancelOrderBase } = useCancelTransactionSteps({
34
74
  collectionAddress,
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ AddressSchema,
4
+ ChainIdSchema,
5
+ QueryArgSchema,
6
+ balanceQueries,
7
+ getIndexerClient,
8
+ } from '../_internal';
9
+ import { queryOptions, useQuery } from '@tanstack/react-query';
10
+ import { useConfig } from './useConfig';
11
+ import type { GetTokenBalancesDetailsReturn } from '@0xsequence/indexer';
12
+ import type { SdkConfig } from '../../types';
13
+
14
+ const filterSchema = z.object({
15
+ accountAddresses: z.array(AddressSchema),
16
+ contractWhitelist: z.array(AddressSchema).optional(),
17
+ omitNativeBalances: z.boolean(),
18
+ });
19
+
20
+ const useCollectionBalanceDetailsArgsSchema = z.object({
21
+ chainId: ChainIdSchema.pipe(z.coerce.number()),
22
+ filter: filterSchema,
23
+ query: QueryArgSchema.optional(),
24
+ });
25
+
26
+ export type CollectionBalanceFilter = z.infer<typeof filterSchema>;
27
+ export type UseCollectionBalanceDetailsArgs = z.input<
28
+ typeof useCollectionBalanceDetailsArgsSchema
29
+ >;
30
+
31
+ const fetchCollectionBalanceDetails = async (
32
+ args: UseCollectionBalanceDetailsArgs,
33
+ indexerClient: Awaited<ReturnType<typeof getIndexerClient>>,
34
+ ) => {
35
+ const promises = args.filter.accountAddresses.map((accountAddress) =>
36
+ indexerClient.getTokenBalancesDetails({
37
+ filter: {
38
+ accountAddresses: [accountAddress],
39
+ contractWhitelist: args.filter.contractWhitelist,
40
+ omitNativeBalances: args.filter.omitNativeBalances,
41
+ },
42
+ }),
43
+ );
44
+
45
+ const responses = await Promise.all(promises);
46
+ const mergedResponse = responses.reduce<GetTokenBalancesDetailsReturn>(
47
+ (acc, curr) => {
48
+ if (!curr) return acc;
49
+ return {
50
+ page: curr.page,
51
+ nativeBalances: [
52
+ ...(acc.nativeBalances || []),
53
+ ...(curr.nativeBalances || []),
54
+ ],
55
+ balances: [...(acc.balances || []), ...(curr.balances || [])],
56
+ };
57
+ },
58
+ { page: {}, nativeBalances: [], balances: [] },
59
+ );
60
+
61
+ if (!mergedResponse) {
62
+ throw new Error('Failed to fetch collection balance details');
63
+ }
64
+
65
+ return mergedResponse;
66
+ };
67
+
68
+ export const collectionBalanceDetailsOptions = (
69
+ args: UseCollectionBalanceDetailsArgs,
70
+ config: SdkConfig,
71
+ ) => {
72
+ const parsedArgs = useCollectionBalanceDetailsArgsSchema.parse(args);
73
+ const indexerClient = getIndexerClient(parsedArgs.chainId, config);
74
+
75
+ return queryOptions({
76
+ queryKey: [...balanceQueries.collectionBalanceDetails, args, config],
77
+ queryFn: () => fetchCollectionBalanceDetails(parsedArgs, indexerClient),
78
+ ...args.query,
79
+ });
80
+ };
81
+
82
+ export const useCollectionBalanceDetails = (
83
+ args: UseCollectionBalanceDetailsArgs,
84
+ ) => {
85
+ const config = useConfig();
86
+ return useQuery(collectionBalanceDetailsOptions(args, config));
87
+ };
@@ -9,7 +9,7 @@ import type {
9
9
  Order,
10
10
  OrderbookKind,
11
11
  } from '../../../_internal';
12
- import { useCurrency, useHighestOffer } from '../../../hooks';
12
+ import { useCurrency } from '../../../hooks';
13
13
  import SvgDiamondEyeIcon from '../../icons/DiamondEye';
14
14
  import ChessTileImage from '../../images/chess-tile.png';
15
15
  import { ActionButton } from '../_internals/action-button/ActionButton';
@@ -95,13 +95,8 @@ export function CollectibleCard({
95
95
  onCannotPerformAction,
96
96
  }: CollectibleCardProps) {
97
97
  const collectibleMetadata = lowestListing?.metadata;
98
+ const highestOffer = lowestListing?.offer
98
99
  const [imageLoadingError, setImageLoadingError] = useState(false);
99
- const { data: highestOffer, isLoading: highestOfferLoading } =
100
- useHighestOffer({
101
- chainId: String(chainId),
102
- collectionAddress,
103
- tokenId: collectibleId,
104
- });
105
100
 
106
101
  const { data: lowestListingCurrency } = useCurrency({
107
102
  chainId,
@@ -111,13 +106,13 @@ export function CollectibleCard({
111
106
  enabled: !!lowestListing?.order?.priceCurrencyAddress,
112
107
  },
113
108
  });
114
- if (highestOfferLoading || cardLoading) {
109
+ if (cardLoading) {
115
110
  return <CollectibleSkeleton />;
116
111
  }
117
112
 
118
113
  const action = (
119
114
  balance
120
- ? (highestOffer?.order && CollectibleCardAction.SELL) ||
115
+ ? (highestOffer && CollectibleCardAction.SELL) ||
121
116
  (!lowestListing?.order && CollectibleCardAction.LIST) ||
122
117
  CollectibleCardAction.TRANSFER
123
118
  : (lowestListing?.order && CollectibleCardAction.BUY) ||
@@ -158,6 +153,8 @@ export function CollectibleCard({
158
153
  <IconButton
159
154
  as="a"
160
155
  href={externalUrl}
156
+ target="_blank"
157
+ rel="noopener noreferrer"
161
158
  size="sm"
162
159
  backdropFilter="blur"
163
160
  variant="glass"
@@ -165,6 +162,7 @@ export function CollectibleCard({
165
162
  e.stopPropagation();
166
163
  }}
167
164
  position="absolute"
165
+ zIndex="20"
168
166
  top="2"
169
167
  left="2"
170
168
  icon={SvgDiamondEyeIcon}
@@ -181,8 +179,8 @@ export function CollectibleCard({
181
179
  <Footer
182
180
  name={name || ''}
183
181
  type={collectionType}
184
- onOfferClick={() => onOfferClick?.({ order: highestOffer?.order })}
185
- highestOffer={highestOffer?.order}
182
+ onOfferClick={() => onOfferClick?.({ order: highestOffer })}
183
+ highestOffer={highestOffer}
186
184
  lowestListingPriceAmount={lowestListing?.order?.priceAmount}
187
185
  lowestListingCurrency={lowestListingCurrency}
188
186
  balance={balance}
@@ -202,7 +200,7 @@ export function CollectibleCard({
202
200
  tokenId={collectibleId}
203
201
  orderbookKind={orderbookKind}
204
202
  action={action}
205
- highestOffer={highestOffer?.order}
203
+ highestOffer={highestOffer}
206
204
  lowestListing={lowestListing?.order}
207
205
  owned={!!balance}
208
206
  onCannotPerformAction={onCannotPerformAction}
@@ -9,7 +9,7 @@ export const collectibleCard = style([
9
9
  border: '1px solid hsla(247, 100%, 75%, 1)',
10
10
  boxShadow: '0px 0px 0px 1px hsla(247, 100%, 75%, 1)',
11
11
  },
12
- ':focus': {
12
+ ':focus-visible': {
13
13
  border: '1px solid hsla(247, 100%, 75%, 1)',
14
14
  boxShadow: '0px 0px 0px 2px hsla(247, 100%, 75%, 1)',
15
15
  outline: '4px solid hsla(254, 100%, 57%, 1)',
@@ -13,8 +13,8 @@ const Svg = () => (
13
13
  >
14
14
  <title id="cart-title">Cart Icon</title>
15
15
  <path
16
- fill-rule="evenodd"
17
- clip-rule="evenodd"
16
+ fillRule="evenodd"
17
+ clipRule="evenodd"
18
18
  d="M2.5 3.46836C2.5 3.20969 2.72366 3 2.99955 3H5.16925C5.88938 3 6.5077 3.48022 6.64172 4.14359L8.33188 12.5093C8.37655 12.7304 8.58266 12.8905 8.8227 12.8905H16.987C17.2629 12.8905 17.4866 13.1002 17.4866 13.3589C17.4866 13.6175 17.2629 13.8272 16.987 13.8272H8.8227C8.10257 13.8272 7.48425 13.347 7.35023 12.6836L5.66007 4.31793C5.6154 4.0968 5.40929 3.93673 5.16925 3.93673H2.99955C2.72366 3.93673 2.5 3.72704 2.5 3.46836Z"
19
19
  fill="white"
20
20
  />
@@ -117,17 +117,20 @@ export const useBuyCollectable = ({
117
117
  enableSwapPayments: !!input.checkoutOptions.swap,
118
118
  creditCardProviders: input.checkoutOptions.nftCheckout || [],
119
119
  onSuccess: (hash: string) => {
120
+ callbacks?.onSuccess?.({ hash: hash as Hash });
121
+ },
122
+ onError: callbacks?.onError,
123
+ onClose: () => {
120
124
  invalidateQueries([
121
125
  collectableKeys.listings,
126
+ collectableKeys.lowestListings,
122
127
  collectableKeys.listingsCount,
123
128
  collectableKeys.lists,
124
129
  collectableKeys.userBalances,
125
130
  balanceQueries.all,
131
+ balanceQueries.collectionBalanceDetails,
126
132
  ]);
127
- callbacks?.onSuccess?.({ hash: hash as Hash });
128
- },
129
- onError: callbacks?.onError,
130
- onClose: () => {
133
+
131
134
  buyModal$.close();
132
135
  },
133
136
  });
@@ -62,7 +62,11 @@ const useHandleTransfer = () => {
62
62
  collectibleId,
63
63
  price: undefined,
64
64
  type: TransactionType.TRANSFER,
65
- queriesToInvalidate: [balanceQueries.all, collectableKeys.userBalances],
65
+ queriesToInvalidate: [
66
+ balanceQueries.all,
67
+ balanceQueries.collectionBalanceDetails,
68
+ collectableKeys.userBalances,
69
+ ],
66
70
  });
67
71
  } catch (error) {
68
72
  transferModal$.view.set('enterReceiverAddress');
@@ -2,7 +2,7 @@ import { Box, Image, Text } from '@0xsequence/design-system';
2
2
  import type { Observable } from '@legendapp/state';
3
3
  import { observer } from '@legendapp/state/react';
4
4
  import { useEffect } from 'react';
5
- import { formatUnits } from 'viem';
5
+ import { formatUnits, zeroAddress } from 'viem';
6
6
  import {
7
7
  CustomSelect,
8
8
  type SelectItem,
@@ -32,12 +32,19 @@ const WaasFeeOptionsSelect = observer(
32
32
  options: FeeOption[];
33
33
  selectedFeeOption$: Observable<FeeOption | undefined>;
34
34
  }) => {
35
- const feeOptions = options
36
- .filter((option) => option.token.contractAddress !== null)
37
- .map((option) => {
38
- const value = option.token.contractAddress ?? '';
39
- return FeeOptionSelectItem({ value, option });
40
- });
35
+ options = options.map((option) => ({
36
+ ...option,
37
+ token: {
38
+ ...option.token,
39
+ contractAddress: option.token.contractAddress || zeroAddress,
40
+ },
41
+ }));
42
+
43
+ const feeOptions = options.map((option) => {
44
+ const value = option.token.contractAddress ?? '';
45
+
46
+ return FeeOptionSelectItem({ value, option });
47
+ });
41
48
 
42
49
  // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
43
50
  useEffect(() => {