@0xsequence/marketplace-sdk 0.4.0 → 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-P3EQRM7K.js → chunk-5GDO4ZBC.js} +5 -4
  2. package/dist/{chunk-P3EQRM7K.js.map → chunk-5GDO4ZBC.js.map} +1 -1
  3. package/dist/{chunk-QTQH5I2E.js → chunk-EVRILXOH.js} +2 -2
  4. package/dist/chunk-EVRILXOH.js.map +1 -0
  5. package/dist/{chunk-GVDLVCR5.js → chunk-GSDUAHL3.js} +1 -1
  6. package/dist/chunk-GSDUAHL3.js.map +1 -0
  7. package/dist/{chunk-6HEMV2OR.js → chunk-IOTKCWOB.js} +890 -644
  8. package/dist/chunk-IOTKCWOB.js.map +1 -0
  9. package/dist/{chunk-AY2MZHZN.js → chunk-KNX2LER4.js} +5 -6
  10. package/dist/{chunk-PAHT6PTD.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-PAHT6PTD.js → chunk-LSMQVX77.js} +5 -6
  14. package/dist/{chunk-AY2MZHZN.js.map → chunk-LSMQVX77.js.map} +1 -1
  15. package/dist/{chunk-6AYHE7ZA.js → chunk-MIYMMP2K.js} +79 -33
  16. package/dist/chunk-MIYMMP2K.js.map +1 -0
  17. package/dist/{chunk-EK5ZSW4M.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 +4 -4
  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 +663 -74
  34. package/dist/react/hooks/index.js +7 -7
  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 +11 -11
  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 +11 -11
  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 +11 -11
  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 +28 -12
  61. package/src/react/_internal/transaction-machine/useTransactionMachine.ts +43 -8
  62. package/src/react/hooks/useBuyCollectable.tsx +13 -6
  63. package/src/react/hooks/useCancelOrder.tsx +14 -7
  64. package/src/react/hooks/useCreateListing.tsx +74 -10
  65. package/src/react/hooks/useCurrencies.tsx +1 -1
  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 +73 -11
  69. package/src/react/hooks/useSell.tsx +72 -11
  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 +62 -23
  88. package/src/react/ui/modals/MakeOfferModal/_store.ts +5 -2
  89. package/src/react/ui/modals/MakeOfferModal/index.tsx +83 -65
  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 -10
  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 +3 -3
  108. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +9 -5
  109. package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +4 -4
  110. package/src/react/ui/modals/_internal/components/transactionPreview/useTransactionPreviewTitle.tsx +1 -1
  111. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +51 -29
  112. package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +2 -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 +2 -2
  115. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getTitle.ts +6 -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 +2 -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-6AYHE7ZA.js.map +0 -1
  125. package/dist/chunk-6HEMV2OR.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-GVDLVCR5.js.map +0 -1
  129. package/dist/chunk-NMCGA2TB.js +0 -98
  130. package/dist/chunk-NMCGA2TB.js.map +0 -1
  131. package/dist/chunk-QTQH5I2E.js.map +0 -1
  132. package/dist/chunk-Y7YO5TLE.js.map +0 -1
  133. /package/dist/{chunk-EK5ZSW4M.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,6 +97,9 @@ const ModalContent = observer(
93
97
  chainId,
94
98
  collectionAddress,
95
99
  orderbookKind,
100
+ enabled: makeOfferModal$.isOpen.get(),
101
+ onSwitchChainRefused: () => makeOfferModal$.close(),
102
+ onApprovalSuccess: () => setApprovalExecutedSuccess(true),
96
103
  onTransactionSent: (hash, orderId) => {
97
104
  if (!hash && !orderId) return;
98
105
 
@@ -105,23 +112,10 @@ const ModalContent = observer(
105
112
  collectibleId,
106
113
  type: TransactionType.OFFER,
107
114
  queriesToInvalidate: collectableKeys.all as unknown as QueryKey[],
115
+ callbacks,
108
116
  });
109
117
  makeOfferModal$.close();
110
118
  },
111
- onSuccess: (hash) => {
112
- if (typeof makeOfferModal$.callbacks?.onSuccess === 'function') {
113
- makeOfferModal$.callbacks.onSuccess(hash);
114
- } else {
115
- console.debug('onSuccess callback not provided:', hash);
116
- }
117
- },
118
- onError: (error) => {
119
- if (typeof makeOfferModal$.callbacks?.onError === 'function') {
120
- makeOfferModal$.callbacks.onError(error);
121
- } else {
122
- console.debug('onError callback not provided:', error);
123
- }
124
- },
125
119
  });
126
120
 
127
121
  const dateToUnixTime = (date: Date) =>
@@ -130,7 +124,7 @@ const ModalContent = observer(
130
124
  const currencyAddress = offerPrice.currency.contractAddress;
131
125
 
132
126
  const { isLoading, steps, refreshSteps } = getMakeOfferSteps({
133
- contractType: collection!.type as ContractType,
127
+ contractType: collection?.type as ContractType,
134
128
  offer: {
135
129
  tokenId: collectibleId,
136
130
  quantity: parseUnits(
@@ -142,16 +136,20 @@ const ModalContent = observer(
142
136
  pricePerToken: offerPrice.amountRaw,
143
137
  },
144
138
  });
139
+ const approvalNeeded = steps?.approval.isPending;
145
140
 
141
+ // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
146
142
  useEffect(() => {
147
143
  if (!currencyAddress) return;
144
+
148
145
  refreshSteps();
149
146
  }, [currencyAddress]);
150
147
 
151
148
  if (collectableIsLoading || collectionIsLoading || currenciesIsLoading) {
152
149
  return (
153
150
  <LoadingModal
154
- store={makeOfferModal$}
151
+ isOpen={makeOfferModal$.isOpen.get()}
152
+ chainId={Number(chainId)}
155
153
  onClose={makeOfferModal$.close}
156
154
  title="Make an offer"
157
155
  />
@@ -161,20 +159,26 @@ const ModalContent = observer(
161
159
  if (collectableIsError || collectionIsError) {
162
160
  return (
163
161
  <ErrorModal
164
- store={makeOfferModal$}
162
+ isOpen={makeOfferModal$.isOpen.get()}
163
+ chainId={Number(chainId)}
165
164
  onClose={makeOfferModal$.close}
166
165
  title="Make an offer"
167
166
  />
168
167
  );
169
168
  }
170
169
 
170
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
171
171
  const handleStepExecution = async (execute?: any) => {
172
172
  if (!execute) return;
173
173
  try {
174
174
  await refreshSteps();
175
175
  await execute();
176
176
  } catch (error) {
177
- 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
+ }
178
182
  }
179
183
  };
180
184
 
@@ -182,68 +186,82 @@ const ModalContent = observer(
182
186
  {
183
187
  label: 'Approve TOKEN',
184
188
  onClick: () => handleStepExecution(() => steps?.approval.execute()),
185
- hidden: !steps?.approval.isPending,
186
- pending: steps?.approval.isExecuting,
189
+ hidden: !approvalNeeded || approvalExecutedSuccess,
190
+ pending: steps?.approval.isExecuting || isLoading,
187
191
  variant: 'glass' as const,
188
- disabled: makeOfferModal$.invalidQuantity.get(),
192
+ disabled:
193
+ invalidQuantity ||
194
+ isLoading ||
195
+ steps?.transaction.isExecuting ||
196
+ insufficientBalance ||
197
+ offerPrice.amountRaw === '0' ||
198
+ !offerPriceChanged,
189
199
  },
190
200
  {
191
201
  label: 'Make offer',
192
202
  onClick: () => handleStepExecution(() => steps?.transaction.execute()),
203
+
193
204
  pending: steps?.transaction.isExecuting || isLoading,
194
205
  disabled:
195
- steps?.approval.isPending ||
206
+ (!approvalExecutedSuccess && approvalNeeded) ||
196
207
  offerPrice.amountRaw === '0' ||
197
208
  insufficientBalance ||
198
209
  isLoading ||
199
- makeOfferModal$.invalidQuantity.get(),
210
+ invalidQuantity ||
211
+ offerPrice.amountRaw === '0',
200
212
  },
201
213
  ];
202
214
 
203
215
  return (
204
- <ActionModal
205
- store={makeOfferModal$}
206
- onClose={() => makeOfferModal$.close()}
207
- title="Make an offer"
208
- ctas={ctas}
209
- >
210
- <TokenPreview
211
- collectionName={collection?.name}
212
- collectionAddress={collectionAddress}
213
- collectibleId={collectibleId}
214
- chainId={chainId}
215
- />
216
-
217
- <PriceInput
218
- chainId={chainId}
219
- collectionAddress={collectionAddress}
220
- $listingPrice={makeOfferModal$.offerPrice}
221
- checkBalance={{
222
- enabled: true,
223
- callback: (state) => setInsufficientBalance(state),
224
- }}
225
- />
226
-
227
- {collection?.type === ContractType.ERC1155 && (
228
- <QuantityInput
229
- $quantity={makeOfferModal$.quantity}
230
- $invalidQuantity={makeOfferModal$.invalidQuantity}
231
- decimals={collectible?.decimals || 0}
232
- 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}
233
229
  />
234
- )}
235
230
 
236
- {!!offerPrice && (
237
- <FloorPriceText
238
- tokenId={collectibleId}
231
+ <PriceInput
239
232
  chainId={chainId}
240
233
  collectionAddress={collectionAddress}
241
- price={offerPrice}
234
+ $listingPrice={makeOfferModal$.offerPrice}
235
+ onPriceChange={() => makeOfferModal$.offerPriceChanged.set(true)}
236
+ checkBalance={{
237
+ enabled: true,
238
+ callback: (state) => setInsufficientBalance(state),
239
+ }}
242
240
  />
243
- )}
244
241
 
245
- <ExpirationDateSelect $date={makeOfferModal$.expiry} />
246
- </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
+ </>
247
265
  );
248
266
  },
249
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>