@0xsequence/marketplace-sdk 0.3.11 → 0.4.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 (133) hide show
  1. package/dist/{chunk-3LOJQCTV.js → chunk-5GDO4ZBC.js} +5 -4
  2. package/dist/{chunk-3LOJQCTV.js.map → chunk-5GDO4ZBC.js.map} +1 -1
  3. package/dist/{chunk-E3ZFT6WR.js → chunk-EVRILXOH.js} +12 -2
  4. package/dist/chunk-EVRILXOH.js.map +1 -0
  5. package/dist/{chunk-N5F6IEAK.js → chunk-GSDUAHL3.js} +1 -1
  6. package/dist/chunk-GSDUAHL3.js.map +1 -0
  7. package/dist/{chunk-IRN77TJW.js → chunk-IOTKCWOB.js} +925 -667
  8. package/dist/chunk-IOTKCWOB.js.map +1 -0
  9. package/dist/{chunk-KRMV6FJE.js → chunk-KNX2LER4.js} +5 -6
  10. package/dist/{chunk-4FC3IG7C.js.map → chunk-KNX2LER4.js.map} +1 -1
  11. package/dist/{chunk-SBVLWSRZ.js → chunk-LF44FCG5.js} +2 -2
  12. package/dist/{chunk-SBVLWSRZ.js.map → chunk-LF44FCG5.js.map} +1 -1
  13. package/dist/{chunk-4FC3IG7C.js → chunk-LSMQVX77.js} +5 -6
  14. package/dist/{chunk-KRMV6FJE.js.map → chunk-LSMQVX77.js.map} +1 -1
  15. package/dist/{chunk-B3LFJJVS.js → chunk-MIYMMP2K.js} +90 -40
  16. package/dist/chunk-MIYMMP2K.js.map +1 -0
  17. package/dist/{chunk-Z7NLKEXF.js → chunk-QMO2CUNM.js} +2 -2
  18. package/dist/{chunk-L6GSYPCR.js → chunk-RZSZNVEH.js} +5 -5
  19. package/dist/{chunk-L6GSYPCR.js.map → chunk-RZSZNVEH.js.map} +1 -1
  20. package/dist/chunk-T5T6JNB2.js +171 -0
  21. package/dist/chunk-T5T6JNB2.js.map +1 -0
  22. package/dist/chunk-UPLTM63S.js +435 -0
  23. package/dist/chunk-UPLTM63S.js.map +1 -0
  24. package/dist/{chunk-Y7YO5TLE.js → chunk-XXML5K3X.js} +5 -2
  25. package/dist/chunk-XXML5K3X.js.map +1 -0
  26. package/dist/{create-config-CgtmCzvb.d.ts → create-config-8sffBvlt.d.ts} +1 -1
  27. package/dist/index.js +5 -5
  28. package/dist/react/_internal/api/index.js +2 -2
  29. package/dist/react/_internal/index.d.ts +1 -1
  30. package/dist/react/_internal/index.js +3 -3
  31. package/dist/react/_internal/wagmi/index.d.ts +1 -1
  32. package/dist/react/_internal/wagmi/index.js +2 -2
  33. package/dist/react/hooks/index.d.ts +666 -77
  34. package/dist/react/hooks/index.js +8 -8
  35. package/dist/react/index.css +17 -0
  36. package/dist/react/index.css.map +1 -1
  37. package/dist/react/index.d.ts +1 -1
  38. package/dist/react/index.js +12 -12
  39. package/dist/react/ssr/index.js +1 -1
  40. package/dist/react/ssr/index.js.map +1 -1
  41. package/dist/react/ui/components/index.css +17 -0
  42. package/dist/react/ui/components/index.css.map +1 -1
  43. package/dist/react/ui/components/index.js +12 -12
  44. package/dist/react/ui/icons/index.js +4 -4
  45. package/dist/react/ui/icons/index.js.map +1 -1
  46. package/dist/react/ui/index.css +17 -0
  47. package/dist/react/ui/index.css.map +1 -1
  48. package/dist/react/ui/index.js +12 -12
  49. package/dist/react/ui/modals/_internal/components/actionModal/index.css +22 -0
  50. package/dist/react/ui/modals/_internal/components/actionModal/index.css.map +1 -1
  51. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +17 -9
  52. package/dist/react/ui/modals/_internal/components/actionModal/index.js +8 -4
  53. package/dist/react/ui/styles/index.d.ts +1 -1
  54. package/dist/styles/index.d.ts +1 -1
  55. package/dist/styles/index.js +2 -2
  56. package/dist/types/index.js +3 -3
  57. package/dist/utils/index.js +2 -2
  58. package/package.json +25 -25
  59. package/src/react/_internal/api/marketplace-api.ts +3 -2
  60. package/src/react/_internal/transaction-machine/execute-transaction.ts +41 -19
  61. package/src/react/_internal/transaction-machine/useTransactionMachine.ts +43 -8
  62. package/src/react/hooks/useBuyCollectable.tsx +15 -8
  63. package/src/react/hooks/useCancelOrder.tsx +15 -7
  64. package/src/react/hooks/useCreateListing.tsx +75 -11
  65. package/src/react/hooks/useCurrencies.tsx +1 -2
  66. package/src/react/hooks/useCurrencyBalance.tsx +1 -1
  67. package/src/react/hooks/useCurrencyOptions.tsx +1 -1
  68. package/src/react/hooks/useMakeOffer.tsx +74 -12
  69. package/src/react/hooks/useSell.tsx +73 -12
  70. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +1 -7
  71. package/src/react/ui/components/_internals/action-button/types.ts +7 -0
  72. package/src/react/ui/components/_internals/custom-select/CustomSelect.tsx +18 -15
  73. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +5 -7
  74. package/src/react/ui/components/collectible-card/Footer.tsx +5 -7
  75. package/src/react/ui/components/collectible-card/styles.css.ts +1 -1
  76. package/src/react/ui/icons/ArrowUp.tsx +3 -0
  77. package/src/react/ui/icons/Bell.tsx +3 -0
  78. package/src/react/ui/icons/CalendarIcon.tsx +3 -0
  79. package/src/react/ui/icons/DiamondEye.tsx +3 -0
  80. package/src/react/ui/icons/InfoIcon.tsx +3 -0
  81. package/src/react/ui/icons/InventoryIcon.tsx +3 -0
  82. package/src/react/ui/icons/MinusIcon.tsx +3 -0
  83. package/src/react/ui/icons/PlusIcon.tsx +3 -0
  84. package/src/react/ui/icons/PositiveCircleIcon.tsx +3 -0
  85. package/src/react/ui/modals/BuyModal/index.tsx +25 -8
  86. package/src/react/ui/modals/CreateListingModal/_store.ts +5 -2
  87. package/src/react/ui/modals/CreateListingModal/index.tsx +66 -25
  88. package/src/react/ui/modals/MakeOfferModal/_store.ts +5 -2
  89. package/src/react/ui/modals/MakeOfferModal/index.tsx +87 -67
  90. package/src/react/ui/modals/SellModal/index.tsx +107 -57
  91. package/src/react/ui/modals/TransferModal/_store.ts +1 -1
  92. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/index.tsx +4 -2
  93. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/useHandleTransfer.tsx +5 -5
  94. package/src/react/ui/modals/TransferModal/index.tsx +1 -1
  95. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +29 -8
  96. package/src/react/ui/modals/_internal/components/actionModal/ErrorModal.tsx +15 -5
  97. package/src/react/ui/modals/_internal/components/actionModal/LoadingModal.tsx +15 -5
  98. package/src/react/ui/modals/_internal/components/actionModal/store.ts +6 -0
  99. package/src/react/ui/modals/_internal/components/calendar/index.tsx +1 -1
  100. package/src/react/ui/modals/_internal/components/currencyImage/index.tsx +3 -3
  101. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +11 -16
  102. package/src/react/ui/modals/_internal/components/expirationDateSelect/index.tsx +14 -19
  103. package/src/react/ui/modals/_internal/components/priceInput/index.tsx +34 -12
  104. package/src/react/ui/modals/_internal/components/quantityInput/index.tsx +3 -3
  105. package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +7 -4
  106. package/src/react/ui/modals/_internal/components/tokenPreview/index.tsx +1 -0
  107. package/src/react/ui/modals/_internal/components/transaction-footer/index.tsx +10 -8
  108. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +9 -5
  109. package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +7 -4
  110. package/src/react/ui/modals/_internal/components/transactionPreview/useTransactionPreviewTitle.tsx +9 -11
  111. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +60 -31
  112. package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +6 -2
  113. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getFormattedType.ts +1 -1
  114. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getMessage.ts +8 -2
  115. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getTitle.ts +12 -3
  116. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/index.tsx +146 -0
  117. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/store.ts +12 -0
  118. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/styles.css.ts +53 -0
  119. package/src/react/ui/modals/_internal/components/waasFeeOptionsSelect/WaasFeeOptionsSelect.tsx +117 -0
  120. package/src/utils/_internal/error/transaction.ts +12 -2
  121. package/src/utils/price.ts +3 -4
  122. package/tsconfig.json +1 -21
  123. package/tsconfig.tsbuildinfo +1 -1
  124. package/dist/chunk-B3LFJJVS.js.map +0 -1
  125. package/dist/chunk-E3ZFT6WR.js.map +0 -1
  126. package/dist/chunk-FFCNYF3S.js +0 -153
  127. package/dist/chunk-FFCNYF3S.js.map +0 -1
  128. package/dist/chunk-IRN77TJW.js.map +0 -1
  129. package/dist/chunk-N5F6IEAK.js.map +0 -1
  130. package/dist/chunk-NMCGA2TB.js +0 -98
  131. package/dist/chunk-NMCGA2TB.js.map +0 -1
  132. package/dist/chunk-Y7YO5TLE.js.map +0 -1
  133. /package/dist/{chunk-Z7NLKEXF.js.map → chunk-QMO2CUNM.js.map} +0 -0
@@ -1,13 +1,15 @@
1
1
  import { Show, observer } from '@legendapp/state/react';
2
2
  import type { QueryKey } from '@tanstack/react-query';
3
3
  import { useEffect, useState } from 'react';
4
- import { parseUnits, type Hex } from 'viem';
4
+ import { type Hex, parseUnits } from 'viem';
5
5
  import {
6
6
  ContractType,
7
- OrderbookKind,
7
+ type OrderbookKind,
8
8
  collectableKeys,
9
9
  } from '../../../_internal';
10
+ import { TransactionType } from '../../../_internal/transaction-machine/execute-transaction';
10
11
  import { useCollectible, useCollection, useCurrencies } from '../../../hooks';
12
+ import { useCurrencyOptions } from '../../../hooks/useCurrencyOptions';
11
13
  import { useMakeOffer } from '../../../hooks/useMakeOffer';
12
14
  import { ActionModal } from '../_internal/components/actionModal/ActionModal';
13
15
  import { ErrorModal } from '../_internal/components/actionModal/ErrorModal';
@@ -20,8 +22,6 @@ import TokenPreview from '../_internal/components/tokenPreview';
20
22
  import { useTransactionStatusModal } from '../_internal/components/transactionStatusModal';
21
23
  import type { ModalCallbacks } from '../_internal/types';
22
24
  import { makeOfferModal$ } from './_store';
23
- import { TransactionType } from '../../../_internal/transaction-machine/execute-transaction';
24
- import { useCurrencyOptions } from '../../../hooks/useCurrencyOptions';
25
25
 
26
26
  export type ShowMakeOfferModalArgs = {
27
27
  collectionAddress: Hex;
@@ -60,11 +60,15 @@ const ModalContent = observer(
60
60
  collectionAddress,
61
61
  chainId,
62
62
  offerPrice,
63
+ offerPriceChanged,
64
+ invalidQuantity,
63
65
  collectibleId,
64
66
  orderbookKind,
67
+ callbacks,
65
68
  } = state;
66
69
  const [insufficientBalance, setInsufficientBalance] = useState(false);
67
-
70
+ const [approvalExecutedSuccess, setApprovalExecutedSuccess] =
71
+ useState(false);
68
72
  const {
69
73
  data: collectible,
70
74
  isLoading: collectableIsLoading,
@@ -93,33 +97,25 @@ const ModalContent = observer(
93
97
  chainId,
94
98
  collectionAddress,
95
99
  orderbookKind,
96
- onTransactionSent: (hash) => {
97
- if (!hash) return;
100
+ enabled: makeOfferModal$.isOpen.get(),
101
+ onSwitchChainRefused: () => makeOfferModal$.close(),
102
+ onApprovalSuccess: () => setApprovalExecutedSuccess(true),
103
+ onTransactionSent: (hash, orderId) => {
104
+ if (!hash && !orderId) return;
105
+
98
106
  showTransactionStatusModal({
99
107
  hash,
108
+ orderId,
100
109
  price: makeOfferModal$.offerPrice.get(),
101
110
  collectionAddress,
102
111
  chainId,
103
112
  collectibleId,
104
113
  type: TransactionType.OFFER,
105
114
  queriesToInvalidate: collectableKeys.all as unknown as QueryKey[],
115
+ callbacks,
106
116
  });
107
117
  makeOfferModal$.close();
108
118
  },
109
- onSuccess: (hash) => {
110
- if (typeof makeOfferModal$.callbacks?.onSuccess === 'function') {
111
- makeOfferModal$.callbacks.onSuccess(hash);
112
- } else {
113
- console.debug('onSuccess callback not provided:', hash);
114
- }
115
- },
116
- onError: (error) => {
117
- if (typeof makeOfferModal$.callbacks?.onError === 'function') {
118
- makeOfferModal$.callbacks.onError(error);
119
- } else {
120
- console.debug('onError callback not provided:', error);
121
- }
122
- },
123
119
  });
124
120
 
125
121
  const dateToUnixTime = (date: Date) =>
@@ -128,7 +124,7 @@ const ModalContent = observer(
128
124
  const currencyAddress = offerPrice.currency.contractAddress;
129
125
 
130
126
  const { isLoading, steps, refreshSteps } = getMakeOfferSteps({
131
- contractType: collection!.type as ContractType,
127
+ contractType: collection?.type as ContractType,
132
128
  offer: {
133
129
  tokenId: collectibleId,
134
130
  quantity: parseUnits(
@@ -140,16 +136,20 @@ const ModalContent = observer(
140
136
  pricePerToken: offerPrice.amountRaw,
141
137
  },
142
138
  });
139
+ const approvalNeeded = steps?.approval.isPending;
143
140
 
141
+ // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
144
142
  useEffect(() => {
145
143
  if (!currencyAddress) return;
144
+
146
145
  refreshSteps();
147
146
  }, [currencyAddress]);
148
147
 
149
148
  if (collectableIsLoading || collectionIsLoading || currenciesIsLoading) {
150
149
  return (
151
150
  <LoadingModal
152
- store={makeOfferModal$}
151
+ isOpen={makeOfferModal$.isOpen.get()}
152
+ chainId={Number(chainId)}
153
153
  onClose={makeOfferModal$.close}
154
154
  title="Make an offer"
155
155
  />
@@ -159,20 +159,26 @@ const ModalContent = observer(
159
159
  if (collectableIsError || collectionIsError) {
160
160
  return (
161
161
  <ErrorModal
162
- store={makeOfferModal$}
162
+ isOpen={makeOfferModal$.isOpen.get()}
163
+ chainId={Number(chainId)}
163
164
  onClose={makeOfferModal$.close}
164
165
  title="Make an offer"
165
166
  />
166
167
  );
167
168
  }
168
169
 
170
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
169
171
  const handleStepExecution = async (execute?: any) => {
170
172
  if (!execute) return;
171
173
  try {
172
174
  await refreshSteps();
173
175
  await execute();
174
176
  } catch (error) {
175
- makeOfferModal$.callbacks?.onError?.(error as Error);
177
+ if (callbacks?.onError) {
178
+ callbacks.onError(error as Error);
179
+ } else {
180
+ console.debug('onError callback not provided:', error);
181
+ }
176
182
  }
177
183
  };
178
184
 
@@ -180,68 +186,82 @@ const ModalContent = observer(
180
186
  {
181
187
  label: 'Approve TOKEN',
182
188
  onClick: () => handleStepExecution(() => steps?.approval.execute()),
183
- hidden: !steps?.approval.isPending,
184
- pending: steps?.approval.isExecuting,
189
+ hidden: !approvalNeeded || approvalExecutedSuccess,
190
+ pending: steps?.approval.isExecuting || isLoading,
185
191
  variant: 'glass' as const,
186
- disabled: makeOfferModal$.invalidQuantity.get(),
192
+ disabled:
193
+ invalidQuantity ||
194
+ isLoading ||
195
+ steps?.transaction.isExecuting ||
196
+ insufficientBalance ||
197
+ offerPrice.amountRaw === '0' ||
198
+ !offerPriceChanged,
187
199
  },
188
200
  {
189
201
  label: 'Make offer',
190
202
  onClick: () => handleStepExecution(() => steps?.transaction.execute()),
203
+
191
204
  pending: steps?.transaction.isExecuting || isLoading,
192
205
  disabled:
193
- steps?.approval.isPending ||
206
+ (!approvalExecutedSuccess && approvalNeeded) ||
194
207
  offerPrice.amountRaw === '0' ||
195
208
  insufficientBalance ||
196
209
  isLoading ||
197
- makeOfferModal$.invalidQuantity.get(),
210
+ invalidQuantity ||
211
+ offerPrice.amountRaw === '0',
198
212
  },
199
213
  ];
200
214
 
201
215
  return (
202
- <ActionModal
203
- store={makeOfferModal$}
204
- onClose={() => makeOfferModal$.close()}
205
- title="Make an offer"
206
- ctas={ctas}
207
- >
208
- <TokenPreview
209
- collectionName={collection?.name}
210
- collectionAddress={collectionAddress}
211
- collectibleId={collectibleId}
212
- chainId={chainId}
213
- />
214
-
215
- <PriceInput
216
- chainId={chainId}
217
- collectionAddress={collectionAddress}
218
- $listingPrice={makeOfferModal$.offerPrice}
219
- checkBalance={{
220
- enabled: true,
221
- callback: (state) => setInsufficientBalance(state),
222
- }}
223
- />
224
-
225
- {collection?.type === ContractType.ERC1155 && (
226
- <QuantityInput
227
- $quantity={makeOfferModal$.quantity}
228
- $invalidQuantity={makeOfferModal$.invalidQuantity}
229
- decimals={collectible?.decimals || 0}
230
- maxQuantity={String(Number.MAX_SAFE_INTEGER)}
216
+ <>
217
+ <ActionModal
218
+ isOpen={makeOfferModal$.isOpen.get()}
219
+ chainId={Number(chainId)}
220
+ onClose={() => makeOfferModal$.close()}
221
+ title="Make an offer"
222
+ ctas={ctas}
223
+ >
224
+ <TokenPreview
225
+ collectionName={collection?.name}
226
+ collectionAddress={collectionAddress}
227
+ collectibleId={collectibleId}
228
+ chainId={chainId}
231
229
  />
232
- )}
233
230
 
234
- {!!offerPrice && (
235
- <FloorPriceText
236
- tokenId={collectibleId}
231
+ <PriceInput
237
232
  chainId={chainId}
238
233
  collectionAddress={collectionAddress}
239
- price={offerPrice}
234
+ $listingPrice={makeOfferModal$.offerPrice}
235
+ onPriceChange={() => makeOfferModal$.offerPriceChanged.set(true)}
236
+ checkBalance={{
237
+ enabled: true,
238
+ callback: (state) => setInsufficientBalance(state),
239
+ }}
240
240
  />
241
- )}
242
241
 
243
- <ExpirationDateSelect $date={makeOfferModal$.expiry} />
244
- </ActionModal>
242
+ {collection?.type === ContractType.ERC1155 && (
243
+ <QuantityInput
244
+ $quantity={makeOfferModal$.quantity}
245
+ $invalidQuantity={makeOfferModal$.invalidQuantity}
246
+ decimals={collectible?.decimals || 0}
247
+ maxQuantity={String(Number.MAX_SAFE_INTEGER)}
248
+ />
249
+ )}
250
+
251
+ {offerPrice.amountRaw !== '0' &&
252
+ offerPriceChanged &&
253
+ !insufficientBalance && (
254
+ <FloorPriceText
255
+ tokenId={collectibleId}
256
+ chainId={chainId}
257
+ collectionAddress={collectionAddress}
258
+ price={offerPrice}
259
+ />
260
+ )}
261
+
262
+ <ExpirationDateSelect $date={makeOfferModal$.expiry} />
263
+ </ActionModal>
264
+ </>
245
265
  );
246
266
  },
247
267
  );
@@ -11,7 +11,10 @@ import { useCollection, useCurrencies } from '../../../hooks';
11
11
  import { useSell } from '../../../hooks/useSell';
12
12
  import { ErrorModal } from '..//_internal/components/actionModal/ErrorModal';
13
13
  import type { ModalCallbacks } from '..//_internal/types';
14
- import { ActionModal } from '../_internal/components/actionModal/ActionModal';
14
+ import {
15
+ ActionModal,
16
+ ActionModalProps,
17
+ } from '../_internal/components/actionModal/ActionModal';
15
18
  import { LoadingModal } from '../_internal/components/actionModal/LoadingModal';
16
19
  import TokenPreview from '../_internal/components/tokenPreview';
17
20
  import TransactionDetails from '../_internal/components/transactionDetails';
@@ -20,6 +23,8 @@ import { useTransactionStatusModal } from '../_internal/components/transactionSt
20
23
  import { sellModal$ } from './_store';
21
24
  import { TransactionType } from '../../../_internal/transaction-machine/execute-transaction';
22
25
  import { useCurrencyOptions } from '../../../hooks/useCurrencyOptions';
26
+ import { useEffect, useState } from 'react';
27
+ import { MarketplaceKind } from '../../../_internal/api/marketplace.gen';
23
28
 
24
29
  export type ShowSellModalArgs = {
25
30
  chainId: string;
@@ -53,26 +58,60 @@ const ModalContent = observer(
53
58
  }: {
54
59
  showTransactionStatusModal: TransactionStatusModalReturn['show'];
55
60
  }) => {
56
- const { tokenId, collectionAddress, chainId, order } = sellModal$.get();
61
+ const { tokenId, collectionAddress, chainId, order, callbacks } =
62
+ sellModal$.get();
57
63
  const { data: collectible } = useCollection({
58
64
  chainId,
59
65
  collectionAddress,
60
66
  });
61
-
62
- const { sell } = useSell({
67
+ const [approvalExecutedSuccess, setApprovalExecutedSuccess] =
68
+ useState(false);
69
+ const {
70
+ data: collection,
71
+ isLoading: collectionLoading,
72
+ isError: collectionError,
73
+ } = useCollection({
74
+ chainId,
63
75
  collectionAddress,
76
+ });
77
+ const currencyOptions = useCurrencyOptions({ collectionAddress });
78
+ const { data: currencies, isLoading: currenciesLoading } = useCurrencies({
64
79
  chainId,
80
+ currencyOptions,
81
+ });
82
+ const { getSellSteps, isLoading: machineLoading } = useSell({
83
+ collectionAddress,
84
+ chainId,
85
+ enabled: sellModal$.isOpen.get(),
86
+ onSwitchChainRefused: () => {
87
+ sellModal$.close();
88
+ },
89
+ onApprovalSuccess: () => setApprovalExecutedSuccess(true),
65
90
  onTransactionSent: (hash) => {
66
91
  if (!hash) return;
67
92
  showTransactionStatusModal({
68
93
  hash: hash,
69
- price: {
70
- amountRaw: order!.priceAmount,
71
- currency: currencies!.find(
72
- (currency) =>
73
- currency.contractAddress === order!.priceCurrencyAddress,
74
- )!,
75
- },
94
+ price: order
95
+ ? {
96
+ amountRaw: order.priceAmount,
97
+ currency: currencies?.find(
98
+ (currency) =>
99
+ currency.contractAddress === order.priceCurrencyAddress,
100
+ ) ?? {
101
+ chainId: Number(chainId),
102
+ contractAddress: order.priceCurrencyAddress,
103
+ name: 'Unknown',
104
+ symbol: 'UNK',
105
+ decimals: 18,
106
+ imageUrl: '',
107
+ exchangeRate: 0,
108
+ defaultChainCurrency: false,
109
+ nativeCurrency: false,
110
+ createdAt: new Date().toISOString(),
111
+ updatedAt: new Date().toISOString(),
112
+ },
113
+ }
114
+ : undefined,
76
115
  collectionAddress,
77
116
  chainId,
78
117
  collectibleId: tokenId,
@@ -81,43 +120,48 @@ const ModalContent = observer(
81
120
  ...collectableKeys.all,
82
121
  balanceQueries.all,
83
122
  ] as unknown as QueryKey[],
123
+ callbacks,
84
124
  });
85
125
  sellModal$.close();
86
126
  },
87
- onSuccess: (hash) => {
88
- if (typeof sellModal$.callbacks?.onSuccess === 'function') {
89
- sellModal$.callbacks.onSuccess(hash);
90
- } else {
91
- console.debug('onSuccess callback not provided:', hash);
92
- }
93
- },
94
- onError: (error) => {
95
- if (typeof sellModal$.callbacks?.onError === 'function') {
96
- sellModal$.callbacks.onError(error);
97
- } else {
98
- console.debug('onError callback not provided:', error);
99
- }
100
- },
101
127
  });
102
128
 
103
- const {
104
- data: collection,
105
- isLoading: collectionLoading,
106
- isError: collectionError,
107
- } = useCollection({
108
- chainId,
109
- collectionAddress,
110
- });
111
- const currencyOptions = useCurrencyOptions({ collectionAddress });
112
- const { data: currencies, isLoading: currenciesLoading } = useCurrencies({
113
- chainId,
114
- currencyOptions,
129
+ const { isLoading, steps, refreshSteps } = getSellSteps({
130
+ orderId: order?.orderId ?? '',
131
+ marketplace: order?.marketplace as MarketplaceKind,
132
+ quantity: order?.quantityRemaining
133
+ ? parseUnits(
134
+ order.quantityRemaining,
135
+ collectible?.decimals || 0,
136
+ ).toString()
137
+ : '1',
115
138
  });
116
139
 
117
- if (collectionLoading || currenciesLoading) {
140
+
141
+ useEffect(() => {
142
+ refreshSteps();
143
+ }, [order, machineLoading]);
144
+
145
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
146
+ const handleStepExecution = async (execute?: any) => {
147
+ if (!execute) return;
148
+ try {
149
+ await refreshSteps();
150
+ await execute();
151
+ } catch (error) {
152
+ if (callbacks?.onError) {
153
+ callbacks.onError(error as Error);
154
+ } else {
155
+ console.debug('onError callback not provided:', error);
156
+ }
157
+ }
158
+ };
159
+
160
+ if (collectionLoading || currenciesLoading || machineLoading) {
118
161
  return (
119
162
  <LoadingModal
120
- store={sellModal$}
163
+ isOpen={sellModal$.isOpen.get()}
164
+ chainId={Number(chainId)}
121
165
  onClose={sellModal$.close}
122
166
  title="You have an offer"
123
167
  />
@@ -127,38 +171,44 @@ const ModalContent = observer(
127
171
  if (collectionError || order === undefined) {
128
172
  return (
129
173
  <ErrorModal
130
- store={sellModal$}
174
+ isOpen={sellModal$.isOpen.get()}
175
+ chainId={Number(chainId)}
131
176
  onClose={sellModal$.close}
132
177
  title="You have an offer"
133
178
  />
134
179
  );
135
180
  }
136
181
 
182
+ const approvalNeeded = steps?.approval.isPending;
183
+
137
184
  const currency = currencies?.find(
138
185
  (c) => c.contractAddress === order?.priceCurrencyAddress,
139
186
  );
140
187
 
188
+ const ctas = [
189
+ {
190
+ label: 'Approve TOKEN',
191
+ onClick: () => handleStepExecution(() => steps?.approval.execute()),
192
+ hidden: !approvalNeeded || approvalExecutedSuccess,
193
+ pending: steps?.approval.isExecuting || isLoading,
194
+ variant: 'glass' as const,
195
+ disabled: isLoading || steps?.transaction.isExecuting,
196
+ },
197
+ {
198
+ label: 'Accept',
199
+ onClick: () => handleStepExecution(() => steps?.transaction.execute()),
200
+ pending: steps?.transaction.isExecuting || isLoading,
201
+ disabled: (!approvalExecutedSuccess && approvalNeeded) || isLoading,
202
+ },
203
+ ] satisfies ActionModalProps['ctas'];
204
+
141
205
  return (
142
206
  <ActionModal
143
- store={sellModal$}
207
+ isOpen={sellModal$.isOpen.get()}
208
+ chainId={Number(chainId)}
144
209
  onClose={sellModal$.close}
145
210
  title="You have an offer"
146
- ctas={[
147
- {
148
- label: 'Accept',
149
- onClick: () =>
150
- sell({
151
- orderId: order?.orderId,
152
- marketplace: order?.marketplace,
153
- quantity: order?.quantityRemaining
154
- ? parseUnits(
155
- order.quantityRemaining,
156
- collectible?.decimals || 0,
157
- ).toString()
158
- : '1',
159
- }),
160
- },
161
- ]}
211
+ ctas={ctas}
162
212
  >
163
213
  <TransactionHeader
164
214
  title="Offer received"
@@ -2,7 +2,7 @@ import { observable } from '@legendapp/state';
2
2
  import type { Hex } from 'viem';
3
3
  import type { ShowTransferModalArgs } from '.';
4
4
  import type { CollectionType } from '../../../_internal';
5
- import { ModalCallbacks } from '../_internal/types';
5
+ import type { ModalCallbacks } from '../_internal/types';
6
6
 
7
7
  export interface TransferModalState {
8
8
  isOpen: boolean;
@@ -23,11 +23,13 @@ const EnterWalletAddressView = () => {
23
23
  chainId,
24
24
  contractAddress: collectionAddress,
25
25
  tokenId: collectibleId,
26
- accountAddress: address!,
26
+ accountAddress: address ?? '',
27
27
  query: { enabled: !!address },
28
28
  });
29
29
  const balanceAmount = tokenBalance?.pages[0].balances[0].balance;
30
- const insufficientBalance: boolean = $quantity.get() > balanceAmount!;
30
+ const insufficientBalance: boolean = balanceAmount
31
+ ? $quantity.get() > balanceAmount
32
+ : true;
31
33
  const { data: collection } = useCollection({
32
34
  collectionAddress,
33
35
  chainId,
@@ -1,12 +1,12 @@
1
+ import type { QueryKey } from '@tanstack/react-query';
1
2
  import type { Hex } from 'viem';
2
3
  import { ContractType } from '../../../../../../types';
3
- import { useTransferTokens } from '../../../../../hooks';
4
- import { transferModal$ } from '../../_store';
5
4
  import { InvalidContractTypeError } from '../../../../../../utils/_internal/error/transaction';
6
- import { useTransactionStatusModal } from '../../../_internal/components/transactionStatusModal';
7
- import { TransactionType } from '../../../../../_internal/transaction-machine/execute-transaction';
8
5
  import { balanceQueries } from '../../../../../_internal';
9
- import { QueryKey } from '@tanstack/react-query';
6
+ import { TransactionType } from '../../../../../_internal/transaction-machine/execute-transaction';
7
+ import { useTransferTokens } from '../../../../../hooks';
8
+ import { useTransactionStatusModal } from '../../../_internal/components/transactionStatusModal';
9
+ import { transferModal$ } from '../../_store';
10
10
 
11
11
  const useHandleTransfer = () => {
12
12
  const {
@@ -4,11 +4,11 @@ import { Close, Content, Overlay, Portal, Root } from '@radix-ui/react-dialog';
4
4
  import type { Hex } from 'viem';
5
5
  import { useAccount } from 'wagmi';
6
6
  import { useSwitchChainModal } from '../_internal/components/switchChainModal';
7
+ import type { ModalCallbacks } from '../_internal/types';
7
8
  import { transferModal$ } from './_store';
8
9
  import EnterWalletAddressView from './_views/enterWalletAddress';
9
10
  import FollowWalletInstructionsView from './_views/followWalletInstructions';
10
11
  import { closeButton, dialogOverlay, transferModalContent } from './styles.css';
11
- import { ModalCallbacks } from '../_internal/types';
12
12
 
13
13
  export type ShowTransferModalArgs = {
14
14
  collectionAddress: Hex;
@@ -1,29 +1,29 @@
1
1
  'use client';
2
2
 
3
3
  import type React from 'react';
4
- import type { ComponentProps } from 'react';
4
+ import { useState, type ComponentProps } from 'react';
5
5
 
6
6
  import {
7
7
  Box,
8
8
  Button,
9
9
  CloseIcon,
10
10
  IconButton,
11
+ Spinner,
11
12
  Text,
12
13
  } from '@0xsequence/design-system';
13
- import type { Observable } from '@legendapp/state';
14
14
  import { observer } from '@legendapp/state/react';
15
15
  import { Close, Content, Overlay, Portal, Root } from '@radix-ui/react-dialog';
16
16
  import { getProviderEl } from '../../../../../_internal';
17
- import type { ActionModalState } from './store';
18
17
  import {
19
18
  closeButton,
20
19
  cta as ctaStyle,
21
20
  dialogContent,
22
21
  dialogOverlay,
23
22
  } from './styles.css';
23
+ import WaasFeeOptionsBox from '../waasFeeOptionsBox';
24
24
 
25
25
  export interface ActionModalProps {
26
- store: Observable<ActionModalState>;
26
+ isOpen: boolean;
27
27
  onClose: () => void;
28
28
  title: string;
29
29
  children: React.ReactNode;
@@ -35,12 +35,15 @@ export interface ActionModalProps {
35
35
  hidden?: boolean;
36
36
  variant?: ComponentProps<typeof Button>['variant'];
37
37
  }[];
38
+ chainId: number;
38
39
  }
39
40
 
40
41
  export const ActionModal = observer(
41
- ({ store, onClose, title, children, ctas }: ActionModalProps) => {
42
+ ({ isOpen, onClose, title, children, ctas, chainId }: ActionModalProps) => {
43
+ const [isSelectingFees, setIsSelectingFees] = useState(false);
44
+
42
45
  return (
43
- <Root open={store.isOpen.get()}>
46
+ <Root open={isOpen && !!chainId}>
44
47
  <Portal container={getProviderEl()}>
45
48
  <Overlay className={dialogOverlay} />
46
49
  <Content className={dialogContent.narrow}>
@@ -77,15 +80,33 @@ export const ActionModal = observer(
77
80
  }}
78
81
  variant={cta.variant || 'primary'}
79
82
  pending={cta.pending}
80
- disabled={cta.disabled}
83
+ disabled={cta.disabled || isSelectingFees}
81
84
  size="lg"
82
85
  width="full"
83
- label={cta.label}
86
+ label={
87
+ <Box
88
+ display="flex"
89
+ alignItems="center"
90
+ gap="2"
91
+ justifyContent="center"
92
+ >
93
+ {cta.pending && <Spinner size="sm" />}
94
+
95
+ {cta.label}
96
+ </Box>
97
+ }
84
98
  />
85
99
  ),
86
100
  )}
87
101
  </Box>
88
102
  </Box>
103
+
104
+ <WaasFeeOptionsBox
105
+ chainId={chainId}
106
+ onFeeOptionsLoaded={() => setIsSelectingFees(true)}
107
+ onFeeOptionConfirmed={() => setIsSelectingFees(false)}
108
+ />
109
+
89
110
  <Close className={closeButton} asChild onClick={onClose}>
90
111
  <IconButton size="xs" aria-label="Close modal" icon={CloseIcon} />
91
112
  </Close>
@@ -1,16 +1,26 @@
1
1
  import { Box } from '@0xsequence/design-system';
2
- import type { Observable } from '@legendapp/state';
3
2
  import { ActionModal } from './ActionModal';
4
- import type { ActionModalState } from './store';
5
3
 
6
4
  interface ErrorModalProps {
7
- store: Observable<ActionModalState>;
5
+ isOpen: boolean;
6
+ chainId: number;
8
7
  onClose: () => void;
9
8
  title: string;
10
9
  }
11
10
 
12
- export const ErrorModal = ({ store, onClose, title }: ErrorModalProps) => (
13
- <ActionModal store={store} onClose={onClose} title={title} ctas={[]}>
11
+ export const ErrorModal = ({
12
+ isOpen,
13
+ chainId,
14
+ onClose,
15
+ title,
16
+ }: ErrorModalProps) => (
17
+ <ActionModal
18
+ isOpen={isOpen}
19
+ chainId={chainId}
20
+ onClose={onClose}
21
+ title={title}
22
+ ctas={[]}
23
+ >
14
24
  <Box display="flex" justifyContent="center" alignItems="center" padding="4">
15
25
  Error loading item details
16
26
  </Box>