@0xsequence/marketplace-sdk 0.7.0 → 0.8.0

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 (228) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.changeset/fuzzy-forks-smoke.md +5 -0
  4. package/CHANGELOG.md +7 -0
  5. package/dist/chunk-2MSBZYLW.js +128 -0
  6. package/dist/chunk-2MSBZYLW.js.map +1 -0
  7. package/dist/{chunk-MPBN3E54.js → chunk-2VHHJNXY.js} +3 -3
  8. package/dist/{chunk-FBUMNJQ4.js → chunk-3II5GLHE.js} +2 -2
  9. package/dist/chunk-3JU7SQVE.js +182 -0
  10. package/dist/chunk-3JU7SQVE.js.map +1 -0
  11. package/dist/{chunk-XNA64MZQ.js → chunk-BCO4CYE4.js} +2 -2
  12. package/dist/{chunk-4XK7XNJ7.js → chunk-BNAUZXPV.js} +73 -2
  13. package/dist/chunk-BNAUZXPV.js.map +1 -0
  14. package/dist/{chunk-Q5URKSC4.js → chunk-FMEEJFAF.js} +1 -1
  15. package/dist/{chunk-BBASZVT3.js → chunk-GBQVYNCD.js} +5 -6
  16. package/dist/chunk-GBQVYNCD.js.map +1 -0
  17. package/dist/{chunk-3AKOPSON.js → chunk-IZ44XPBH.js} +2 -9
  18. package/dist/chunk-IZ44XPBH.js.map +1 -0
  19. package/dist/{chunk-EAJ5K7QV.js → chunk-Q5RKAMYF.js} +3 -4
  20. package/dist/chunk-Q5RKAMYF.js.map +1 -0
  21. package/dist/{chunk-RBEPPVLT.js → chunk-QKGZXS4T.js} +147 -357
  22. package/dist/chunk-QKGZXS4T.js.map +1 -0
  23. package/dist/{chunk-HTFORA4Q.js → chunk-SBI52HTX.js} +1880 -1010
  24. package/dist/chunk-SBI52HTX.js.map +1 -0
  25. package/dist/{chunk-XTGMMNV5.js → chunk-WHFXVREI.js} +2 -2
  26. package/dist/{index-CzTANLaA.d.ts → index-CnaFSNE9.d.ts} +6 -7
  27. package/dist/index.css +113 -31
  28. package/dist/index.css.map +1 -1
  29. package/dist/index.d.ts +2 -4
  30. package/dist/index.js +9 -9
  31. package/dist/listCollectibles-B0tbqnRd.d.ts +155 -0
  32. package/dist/react/_internal/api/index.d.ts +28 -2
  33. package/dist/react/_internal/api/index.js +3 -1
  34. package/dist/react/_internal/databeat/index.js +11 -10
  35. package/dist/react/_internal/index.d.ts +2 -3
  36. package/dist/react/_internal/index.js +4 -4
  37. package/dist/react/_internal/wagmi/index.js +2 -2
  38. package/dist/react/hooks/index.d.ts +259 -674
  39. package/dist/react/hooks/index.js +10 -19
  40. package/dist/react/hooks/options/index.d.ts +1 -2
  41. package/dist/react/hooks/options/index.js +4 -4
  42. package/dist/react/index.d.ts +7 -7
  43. package/dist/react/index.js +14 -23
  44. package/dist/react/queries/index.d.ts +25 -0
  45. package/dist/react/queries/index.js +48 -0
  46. package/dist/react/queries/index.js.map +1 -0
  47. package/dist/react/ssr/index.js +3 -3
  48. package/dist/react/ui/components/collectible-card/index.d.ts +1 -6
  49. package/dist/react/ui/components/collectible-card/index.js +14 -13
  50. package/dist/react/ui/components/marketplace-logos/index.js +1 -1
  51. package/dist/react/ui/icons/index.js +7 -7
  52. package/dist/react/ui/index.d.ts +18 -22
  53. package/dist/react/ui/index.js +14 -13
  54. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +6 -11
  55. package/dist/react/ui/modals/_internal/components/actionModal/index.js +11 -10
  56. package/dist/types/index.js +2 -2
  57. package/dist/{types-Ct1uCT3M.d.ts → types-o_pKUpQG.d.ts} +2 -5
  58. package/dist/utils/abi/index.js +5 -5
  59. package/dist/utils/index.d.ts +1 -5
  60. package/dist/utils/index.js +9 -9
  61. package/package.json +22 -16
  62. package/src/react/_internal/api/__mocks__/indexer.msw.ts +3 -1
  63. package/src/react/_internal/api/__mocks__/marketplace.msw.ts +1 -1
  64. package/src/react/_internal/api/__mocks__/metadata.msw.ts +14 -12
  65. package/src/react/_internal/api/index.ts +1 -0
  66. package/src/react/_internal/api/laos-api.ts +103 -0
  67. package/src/react/_internal/api/zod-schema.ts +3 -3
  68. package/src/react/_internal/types.ts +1 -10
  69. package/src/react/_internal/wallet/__tests__/wallet.test.ts +43 -0
  70. package/src/react/_internal/wallet/useWallet.ts +6 -3
  71. package/src/react/hooks/__tests__/useAutoSelectFeeOption.test.tsx +1 -1
  72. package/src/react/hooks/__tests__/useCancelOrder.test.tsx +1 -1
  73. package/src/react/hooks/__tests__/useCancelTransactionSteps.test.tsx +1 -1
  74. package/src/react/hooks/__tests__/useCollectible.test.tsx +2 -2
  75. package/src/react/hooks/__tests__/useCollection.test.tsx +2 -2
  76. package/src/react/hooks/__tests__/useComparePrices.test.tsx +1 -1
  77. package/src/react/hooks/__tests__/useConvertPriceToUSD.test.tsx +1 -1
  78. package/src/react/hooks/__tests__/useCountListingsForCollectible.test.tsx +1 -1
  79. package/src/react/hooks/__tests__/useCountOfCollectables.test.tsx +2 -2
  80. package/src/react/hooks/__tests__/useCountOffersForCollectible.test.tsx +1 -1
  81. package/src/react/hooks/__tests__/useCurrencies.test.tsx +2 -2
  82. package/src/react/hooks/__tests__/useCurrency.test.tsx +2 -2
  83. package/src/react/hooks/__tests__/useFilters.test.tsx +2 -2
  84. package/src/react/hooks/__tests__/useFloorOrder.test.tsx +2 -2
  85. package/src/react/hooks/__tests__/useGenerateCancelTransaction.test.tsx +1 -1
  86. package/src/react/hooks/__tests__/useGenerateListingTransaction.test.tsx +1 -1
  87. package/src/react/hooks/__tests__/useGenerateOfferTransaction.test.tsx +1 -1
  88. package/src/react/hooks/__tests__/useGenerateSellTransaction.test.tsx +1 -1
  89. package/src/react/hooks/__tests__/useListCollectibleActivities.test.tsx +2 -2
  90. package/src/react/hooks/__tests__/useListCollectibles.test.tsx +3 -2
  91. package/src/react/hooks/__tests__/useListCollectiblesPaginated.test.tsx +1 -1
  92. package/src/react/hooks/__tests__/useListCollectionActivities.test.tsx +1 -1
  93. package/src/react/hooks/__tests__/useListListingsForCollectible.test.tsx +1 -1
  94. package/src/react/hooks/__tests__/useListOffersForCollectible.test.tsx +1 -1
  95. package/src/react/hooks/__tests__/useLowestListing.test.tsx +1 -1
  96. package/src/react/hooks/__tests__/useRoyalty.test.tsx +1 -2
  97. package/src/react/hooks/index.ts +0 -1
  98. package/src/react/hooks/options/collectionOptions.ts +2 -3
  99. package/src/react/hooks/useAutoSelectFeeOption.tsx +1 -1
  100. package/src/react/hooks/useCancelOrder.tsx +3 -3
  101. package/src/react/hooks/useCancelTransactionSteps.tsx +1 -1
  102. package/src/react/hooks/useCheckoutOptions.tsx +1 -2
  103. package/src/react/hooks/useCollectible.tsx +2 -3
  104. package/src/react/hooks/useCollectionBalanceDetails.tsx +1 -2
  105. package/src/react/hooks/useComparePrices.tsx +2 -9
  106. package/src/react/hooks/useConvertPriceToUSD.tsx +1 -4
  107. package/src/react/hooks/useCountListingsForCollectible.tsx +1 -2
  108. package/src/react/hooks/useCountOfCollectables.tsx +1 -2
  109. package/src/react/hooks/useCountOffersForCollectible.tsx +1 -2
  110. package/src/react/hooks/useCurrencies.tsx +1 -4
  111. package/src/react/hooks/useCurrency.tsx +3 -8
  112. package/src/react/hooks/useFilters.tsx +5 -4
  113. package/src/react/hooks/useFloorOrder.tsx +1 -2
  114. package/src/react/hooks/useGenerateCancelTransaction.tsx +3 -6
  115. package/src/react/hooks/useGenerateListingTransaction.tsx +2 -3
  116. package/src/react/hooks/useGenerateOfferTransaction.tsx +2 -3
  117. package/src/react/hooks/useGenerateSellTransaction.tsx +3 -6
  118. package/src/react/hooks/useHighestOffer.tsx +1 -1
  119. package/src/react/hooks/useListCollectibles.tsx +33 -61
  120. package/src/react/hooks/useListCollectiblesPaginated.tsx +2 -6
  121. package/src/react/hooks/useListListingsForCollectible.tsx +1 -2
  122. package/src/react/hooks/useListOffersForCollectible.tsx +1 -2
  123. package/src/react/hooks/useLowestListing.tsx +1 -2
  124. package/src/react/hooks/useRoyalty.tsx +3 -8
  125. package/src/react/hooks/useTransferTokens.tsx +2 -2
  126. package/src/react/queries/balanceOfCollectible.ts +9 -32
  127. package/src/react/queries/getTokenSupplies.ts +38 -0
  128. package/src/react/queries/index.ts +5 -0
  129. package/src/react/queries/listCollectibles.ts +96 -0
  130. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +1 -1
  131. package/src/react/ui/components/_internals/action-button/__tests__/ActionButton.test.tsx +1 -1
  132. package/src/react/ui/components/_internals/action-button/components/NonOwnerActions.tsx +4 -3
  133. package/src/react/ui/components/_internals/action-button/components/OwnerActions.tsx +1 -1
  134. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +2 -3
  135. package/src/react/ui/components/collectible-card/__tests__/CollectibleCard.test.tsx +1 -1
  136. package/src/react/ui/modals/BuyModal/ERC1155QuantityModal.tsx +127 -0
  137. package/src/react/ui/modals/BuyModal/Modal.tsx +67 -85
  138. package/src/react/ui/modals/BuyModal/__tests__/Modal.test.tsx +85 -226
  139. package/src/react/ui/modals/BuyModal/__tests__/Modal1155.test.tsx +140 -0
  140. package/src/react/ui/modals/BuyModal/__tests__/store.test.ts +67 -76
  141. package/src/react/ui/modals/BuyModal/hooks/__tests__/useCheckoutOptions.test.tsx +1 -60
  142. package/src/react/ui/modals/BuyModal/hooks/__tests__/useFees.test.tsx +1 -1
  143. package/src/react/ui/modals/BuyModal/hooks/useCheckoutOptions.ts +29 -13
  144. package/src/react/ui/modals/BuyModal/hooks/useLoadData.ts +26 -21
  145. package/src/react/ui/modals/BuyModal/hooks/usePaymentModalParams.ts +200 -0
  146. package/src/react/ui/modals/BuyModal/index.tsx +4 -14
  147. package/src/react/ui/modals/BuyModal/store.ts +71 -76
  148. package/src/react/ui/modals/CreateListingModal/Modal.tsx +71 -7
  149. package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +2 -2
  150. package/src/react/ui/modals/CreateListingModal/hooks/useCreateListing.tsx +1 -1
  151. package/src/react/ui/modals/CreateListingModal/hooks/useGetTokenApproval.ts +1 -1
  152. package/src/react/ui/modals/CreateListingModal/hooks/useTransactionSteps.tsx +9 -5
  153. package/src/react/ui/modals/CreateListingModal/store.ts +7 -2
  154. package/src/react/ui/modals/MakeOfferModal/Modal.tsx +103 -6
  155. package/src/react/ui/modals/MakeOfferModal/__tests__/Modal.test.tsx +1 -1
  156. package/src/react/ui/modals/MakeOfferModal/hooks/useGetTokenApproval.tsx +1 -1
  157. package/src/react/ui/modals/MakeOfferModal/hooks/useMakeOffer.tsx +1 -1
  158. package/src/react/ui/modals/MakeOfferModal/hooks/useTransactionSteps.tsx +8 -4
  159. package/src/react/ui/modals/MakeOfferModal/store.ts +5 -3
  160. package/src/react/ui/modals/SellModal/Modal.tsx +76 -4
  161. package/src/react/ui/modals/SellModal/__tests__/Modal.test.tsx +1 -1
  162. package/src/react/ui/modals/SellModal/hooks/useGetTokenApproval.tsx +1 -1
  163. package/src/react/ui/modals/SellModal/hooks/useSell.tsx +1 -1
  164. package/src/react/ui/modals/SellModal/hooks/useTransactionSteps.tsx +16 -8
  165. package/src/react/ui/modals/SellModal/store.ts +5 -3
  166. package/src/react/ui/modals/TransferModal/_store.ts +15 -3
  167. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/_components/TokenQuantityInput.tsx +58 -0
  168. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/_components/TransferButton.tsx +56 -0
  169. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/_components/WalletAddressInput.tsx +50 -0
  170. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/index.tsx +94 -66
  171. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/useHandleTransfer.tsx +22 -14
  172. package/src/react/ui/modals/TransferModal/index.tsx +72 -38
  173. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +43 -42
  174. package/src/react/ui/modals/_internal/components/actionModal/store.ts +1 -2
  175. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +2 -2
  176. package/src/react/ui/modals/_internal/components/expirationDateSelect/index.tsx +9 -1
  177. package/src/react/ui/modals/_internal/components/floorPriceText/index.tsx +27 -12
  178. package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +1 -1
  179. package/src/react/ui/modals/_internal/components/priceInput/index.tsx +19 -3
  180. package/src/react/ui/modals/_internal/components/quantityInput/index.tsx +3 -0
  181. package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/_components/ActionButtons.tsx +60 -0
  182. package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/_components/BalanceIndicator.tsx +30 -0
  183. package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/index.tsx +126 -0
  184. package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/store.ts +25 -0
  185. package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/useWaasFeeOptionManager.tsx +74 -0
  186. package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +1 -2
  187. package/src/react/ui/modals/_internal/components/switchChainModal/store.ts +1 -2
  188. package/src/react/ui/modals/_internal/components/tokenPreview/index.tsx +1 -1
  189. package/src/react/ui/modals/_internal/components/transaction-footer/index.tsx +4 -7
  190. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +1 -1
  191. package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +1 -1
  192. package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/TransactionStatusModal.test.tsx +1 -1
  193. package/src/react/ui/modals/_internal/components/transactionStatusModal/hooks/useTransactionStatus.ts +1 -1
  194. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +6 -1
  195. package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +2 -2
  196. package/src/react/ui/modals/_internal/components/waasFeeOptionsSelect/WaasFeeOptionsSelect.tsx +10 -31
  197. package/src/react/ui/modals/_internal/hooks/useSelectWaasFeeOptions.ts +53 -0
  198. package/src/react/ui/modals/_internal/types.ts +2 -1
  199. package/src/types/waas-types.ts +38 -0
  200. package/src/utils/network.ts +2 -4
  201. package/test/const.ts +1 -1
  202. package/test/setup.ts +10 -0
  203. package/test/test-utils.tsx +31 -5
  204. package/tsconfig.tsbuildinfo +1 -1
  205. package/dist/chunk-3AKOPSON.js.map +0 -1
  206. package/dist/chunk-4XK7XNJ7.js.map +0 -1
  207. package/dist/chunk-BBASZVT3.js.map +0 -1
  208. package/dist/chunk-EAJ5K7QV.js.map +0 -1
  209. package/dist/chunk-HTFORA4Q.js.map +0 -1
  210. package/dist/chunk-OFY7OFTL.js +0 -458
  211. package/dist/chunk-OFY7OFTL.js.map +0 -1
  212. package/dist/chunk-RBEPPVLT.js.map +0 -1
  213. package/src/react/hooks/__tests__/useGenerateBuyTransaction.test.tsx +0 -172
  214. package/src/react/hooks/useGenerateBuyTransaction.tsx +0 -80
  215. package/src/react/ui/modals/BuyModal/hooks/__tests__/useBuyCollectable.test.tsx +0 -349
  216. package/src/react/ui/modals/BuyModal/hooks/__tests__/useLoadData.test.tsx +0 -185
  217. package/src/react/ui/modals/BuyModal/hooks/useBuyCollectable.ts +0 -170
  218. package/src/react/ui/modals/BuyModal/modals/CheckoutModal.tsx +0 -47
  219. package/src/react/ui/modals/BuyModal/modals/Modal1155.tsx +0 -140
  220. package/src/react/ui/modals/BuyModal/modals/__tests__/CheckoutModal.test.tsx +0 -162
  221. package/src/react/ui/modals/BuyModal/modals/__tests__/Modal1155.test.tsx +0 -327
  222. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/index.tsx +0 -124
  223. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/store.ts +0 -12
  224. /package/dist/{chunk-MPBN3E54.js.map → chunk-2VHHJNXY.js.map} +0 -0
  225. /package/dist/{chunk-FBUMNJQ4.js.map → chunk-3II5GLHE.js.map} +0 -0
  226. /package/dist/{chunk-XNA64MZQ.js.map → chunk-BCO4CYE4.js.map} +0 -0
  227. /package/dist/{chunk-Q5URKSC4.js.map → chunk-FMEEJFAF.js.map} +0 -0
  228. /package/dist/{chunk-XTGMMNV5.js.map → chunk-WHFXVREI.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import type React from 'react';
4
- import { type ComponentProps, useState } from 'react';
4
+ import type { ComponentProps } from 'react';
5
5
 
6
6
  import { Button, Modal, Spinner, Text } from '@0xsequence/design-system';
7
7
  import { observer } from '@legendapp/state/react';
@@ -9,7 +9,6 @@ import { useWallet } from '../../../../../_internal/wallet/useWallet';
9
9
  import { MODAL_OVERLAY_PROPS } from '../consts';
10
10
  import { MODAL_CONTENT_PROPS } from '../consts';
11
11
  import { useSwitchChainModal } from '../switchChainModal';
12
- import WaasFeeOptionsBox from '../waasFeeOptionsBox';
13
12
 
14
13
  export interface ActionModalProps {
15
14
  isOpen: boolean;
@@ -17,7 +16,7 @@ export interface ActionModalProps {
17
16
  title: string;
18
17
  children: React.ReactNode;
19
18
  ctas: {
20
- label: string;
19
+ label: React.ReactNode;
21
20
  onClick: (() => Promise<void>) | (() => void);
22
21
  pending?: boolean;
23
22
  disabled?: boolean;
@@ -29,6 +28,7 @@ export interface ActionModalProps {
29
28
  modalLoading?: boolean;
30
29
  spinnerContainerClassname?: string;
31
30
  disableAnimation?: boolean;
31
+ hideCtas?: boolean;
32
32
  }
33
33
 
34
34
  export const ActionModal = observer(
@@ -42,10 +42,10 @@ export const ActionModal = observer(
42
42
  disableAnimation,
43
43
  modalLoading,
44
44
  spinnerContainerClassname,
45
+ hideCtas,
45
46
  }: ActionModalProps) => {
46
- const [isSelectingFees, setIsSelectingFees] = useState(false);
47
47
  const { show: showSwitchChainModal } = useSwitchChainModal();
48
- const { wallet } = useWallet();
48
+ const { wallet, isLoading, isError } = useWallet();
49
49
 
50
50
  const checkChain = async ({ onSuccess }: { onSuccess: () => void }) => {
51
51
  const walletChainId = await wallet?.getChainId();
@@ -85,53 +85,54 @@ export const ActionModal = observer(
85
85
  {title}
86
86
  </Text>
87
87
 
88
- {modalLoading ? (
88
+ {modalLoading || isLoading || isError ? (
89
89
  <div
90
90
  className={`flex ${spinnerContainerClassname} w-full items-center justify-center`}
91
91
  >
92
- <Spinner size="lg" />
92
+ {isError && (
93
+ <Text className="text-center font-body text-error100 text-small">
94
+ Error loading modal
95
+ </Text>
96
+ )}
97
+ {isLoading && <Spinner size="lg" />}
93
98
  </div>
94
99
  ) : (
95
100
  children
96
101
  )}
97
102
 
98
- <div className="flex w-full flex-col gap-2">
99
- {ctas.map(
100
- (cta) =>
101
- !cta.hidden && (
102
- <Button
103
- className="w-full rounded-[12px] [&>div]:justify-center"
104
- key={cta.label}
105
- onClick={async () => {
106
- await checkChain({
107
- onSuccess: () => {
108
- cta.onClick();
109
- },
110
- });
111
- }}
112
- variant={cta.variant || 'primary'}
113
- pending={cta.pending}
114
- disabled={cta.disabled || isSelectingFees}
115
- size="lg"
116
- data-testid={cta.testid}
117
- label={
118
- <div className="flex items-center justify-center gap-2">
119
- {cta.pending && <Spinner size="sm" />}
103
+ {!hideCtas && !isLoading && !isError && (
104
+ <div className="flex w-full flex-col gap-2">
105
+ {ctas.map(
106
+ (cta) =>
107
+ !cta.hidden && (
108
+ <Button
109
+ className="w-full rounded-[12px] [&>div]:justify-center"
110
+ key={cta.onClick.toString()}
111
+ onClick={async () => {
112
+ await checkChain({
113
+ onSuccess: () => {
114
+ cta.onClick();
115
+ },
116
+ });
117
+ }}
118
+ variant={cta.variant || 'primary'}
119
+ pending={cta.pending}
120
+ disabled={cta.disabled}
121
+ size="lg"
122
+ data-testid={cta.testid}
123
+ label={
124
+ <div className="flex items-center justify-center gap-2">
125
+ {cta.pending && <Spinner size="sm" />}
120
126
 
121
- {cta.label}
122
- </div>
123
- }
124
- />
125
- ),
126
- )}
127
- </div>
127
+ {cta.label}
128
+ </div>
129
+ }
130
+ />
131
+ ),
132
+ )}
133
+ </div>
134
+ )}
128
135
  </div>
129
-
130
- <WaasFeeOptionsBox
131
- chainId={chainId}
132
- onFeeOptionsLoaded={() => setIsSelectingFees(true)}
133
- onFeeOptionConfirmed={() => setIsSelectingFees(false)}
134
- />
135
136
  </Modal>
136
137
  );
137
138
  },
@@ -1,10 +1,9 @@
1
1
  import { type Observable, observable } from '@legendapp/state';
2
2
  import type { Address } from 'viem';
3
- import type { ChainId } from '../../../../../_internal';
4
3
 
5
4
  export interface ActionModalState {
6
5
  isOpen: boolean;
7
- chainId: ChainId | null;
6
+ chainId: number | null;
8
7
  collectionAddress: Address | null;
9
8
  }
10
9
 
@@ -5,7 +5,7 @@ import type { Observable } from '@legendapp/state';
5
5
  import { observer } from '@legendapp/state/react';
6
6
  import { useEffect } from 'react';
7
7
  import type { Hex } from 'viem';
8
- import type { ChainId, Currency } from '../../../../../_internal';
8
+ import type { Currency } from '../../../../../_internal';
9
9
  import { useCurrencies } from '../../../../../hooks';
10
10
  import {
11
11
  CustomSelect,
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  type CurrencyOptionsSelectProps = {
16
16
  collectionAddress: Hex;
17
- chainId: ChainId;
17
+ chainId: number;
18
18
  selectedCurrency$: Observable<Currency | null | undefined>;
19
19
  secondCurrencyAsDefault?: boolean;
20
20
  includeNativeCurrency?: boolean;
@@ -5,6 +5,7 @@ import type { Observable } from '@legendapp/state';
5
5
  import { observer } from '@legendapp/state/react';
6
6
  import { addDays } from 'date-fns';
7
7
  import { useState } from 'react';
8
+ import { cn } from '../../../../../../utils';
8
9
  import CalendarDropdown from '../calendarDropdown';
9
10
 
10
11
  const setToEndOfDay = (date: Date): Date => {
@@ -47,11 +48,13 @@ export type RangeType =
47
48
  type ExpirationDateSelectProps = {
48
49
  className?: string;
49
50
  $date: Observable<Date>;
51
+ disabled?: boolean;
50
52
  };
51
53
 
52
54
  const ExpirationDateSelect = observer(function ExpirationDateSelect({
53
55
  className,
54
56
  $date,
57
+ disabled,
55
58
  }: ExpirationDateSelectProps) {
56
59
  const [calendarDropdownOpen, setCalendarDropdownOpen] = useState(false);
57
60
 
@@ -82,7 +85,12 @@ const ExpirationDateSelect = observer(function ExpirationDateSelect({
82
85
  }
83
86
 
84
87
  return (
85
- <div className="relative w-full">
88
+ <div
89
+ className={cn(
90
+ 'relative w-full',
91
+ disabled && 'pointer-events-none opacity-50',
92
+ )}
93
+ >
86
94
  <Text
87
95
  className="w-full text-left font-body font-medium text-xs"
88
96
  fontWeight={'medium'}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { Text } from '@0xsequence/design-system';
3
+ import { Button, Text } from '@0xsequence/design-system';
4
4
  import type { Hex } from 'viem';
5
5
  import type { Price } from '../../../../../../types';
6
6
  import { useComparePrices, useLowestListing } from '../../../../../hooks';
@@ -10,11 +10,13 @@ export default function FloorPriceText({
10
10
  collectionAddress,
11
11
  tokenId,
12
12
  price,
13
+ onBuyNow,
13
14
  }: {
14
- chainId: string;
15
+ chainId: number;
15
16
  collectionAddress: Hex;
16
17
  tokenId: string;
17
18
  price: Price;
19
+ onBuyNow?: () => void;
18
20
  }) {
19
21
  const { data: listing, isLoading: listingLoading } = useLowestListing({
20
22
  tokenId: tokenId,
@@ -26,6 +28,7 @@ export default function FloorPriceText({
26
28
  });
27
29
 
28
30
  const floorPriceRaw = listing?.order?.priceAmount;
31
+ const floorPriceFormatted = listing?.order?.priceAmountFormatted;
29
32
 
30
33
  const { data: priceComparison, isLoading: comparisonLoading } =
31
34
  useComparePrices({
@@ -50,24 +53,36 @@ export default function FloorPriceText({
50
53
  }
51
54
 
52
55
  let floorPriceDifferenceText = 'Same as floor price';
56
+ let showBuyNowButton = false;
53
57
 
54
58
  if (priceComparison) {
55
59
  if (priceComparison.status === 'same') {
56
60
  floorPriceDifferenceText = 'Same as floor price';
61
+ showBuyNowButton = true;
62
+ } else if (priceComparison.status === 'below') {
63
+ floorPriceDifferenceText = `${priceComparison.percentageDifferenceFormatted}% below floor price`;
57
64
  } else {
58
- floorPriceDifferenceText = `${priceComparison.percentageDifferenceFormatted}% ${
59
- priceComparison.status === 'below' ? 'below' : 'above'
60
- } floor price`;
65
+ floorPriceDifferenceText = `${priceComparison.percentageDifferenceFormatted}% above floor price`;
66
+ showBuyNowButton = true;
61
67
  }
62
68
  }
63
69
 
64
70
  return (
65
- <Text
66
- className="w-full text-left font-body text-sm"
67
- fontWeight={'medium'}
68
- color={'text50'}
69
- >
70
- {floorPriceDifferenceText}
71
- </Text>
71
+ <div className="flex w-full items-center justify-between gap-2">
72
+ <Text className="text-left font-body font-medium text-muted text-xs">
73
+ {floorPriceDifferenceText}
74
+ </Text>
75
+
76
+ {showBuyNowButton && onBuyNow && (
77
+ <Button
78
+ size="xs"
79
+ variant="text"
80
+ className="text-indigo-400 text-xs"
81
+ onClick={onBuyNow}
82
+ >
83
+ Buy for {floorPriceFormatted} {price.currency.symbol}
84
+ </Button>
85
+ )}
86
+ </div>
72
87
  );
73
88
  }
@@ -64,7 +64,7 @@ const createMockPrice = (amount = '0'): Price => ({
64
64
  describe('PriceInput', () => {
65
65
  const defaultProps = {
66
66
  collectionAddress: '0xCollection' as `0x${string}`,
67
- chainId: '1',
67
+ chainId: 1,
68
68
  $price: observable<Price | undefined>(createMockPrice()),
69
69
  includeNativeCurrency: false,
70
70
  secondCurrencyAsDefault: false,
@@ -7,13 +7,14 @@ import { useEffect, useRef, useState } from 'react';
7
7
  import { type Hex, parseUnits } from 'viem';
8
8
  import { useAccount } from 'wagmi';
9
9
  import type { Price } from '../../../../../../types';
10
+ import { cn } from '../../../../../../utils';
10
11
  import { useCurrencyBalance } from '../../../../../hooks/useCurrencyBalance';
11
12
  import CurrencyImage from '../currencyImage';
12
13
  import CurrencyOptionsSelect from '../currencyOptionsSelect';
13
14
 
14
15
  type PriceInputProps = {
15
16
  collectionAddress: Hex;
16
- chainId: string;
17
+ chainId: number;
17
18
  secondCurrencyAsDefault?: boolean;
18
19
  $price: Observable<Price | undefined>;
19
20
  includeNativeCurrency?: boolean;
@@ -22,6 +23,7 @@ type PriceInputProps = {
22
23
  enabled: boolean;
23
24
  callback: (state: boolean) => void;
24
25
  };
26
+ disabled?: boolean;
25
27
  };
26
28
 
27
29
  export default function PriceInput({
@@ -32,15 +34,23 @@ export default function PriceInput({
32
34
  checkBalance,
33
35
  secondCurrencyAsDefault,
34
36
  includeNativeCurrency,
37
+ disabled,
35
38
  }: PriceInputProps) {
36
39
  const { address: accountAddress } = useAccount();
40
+ const inputRef = useRef<HTMLInputElement>(null);
37
41
  const currencyDecimals = use$($price.currency.decimals);
38
42
  const currencyAddress = use$($price.currency.contractAddress);
39
43
  const priceAmountRaw = use$($price.amountRaw);
40
44
 
45
+ useEffect(() => {
46
+ if (inputRef.current) {
47
+ inputRef.current.focus();
48
+ }
49
+ }, []);
50
+
41
51
  const { data: balance, isSuccess: isBalanceSuccess } = useCurrencyBalance({
42
52
  currencyAddress: currencyAddress as undefined | Hex,
43
- chainId: Number(chainId),
53
+ chainId,
44
54
  userAddress: accountAddress,
45
55
  });
46
56
 
@@ -93,13 +103,19 @@ export default function PriceInput({
93
103
  };
94
104
 
95
105
  return (
96
- <div className="relative flex w-full flex-col">
106
+ <div
107
+ className={cn(
108
+ 'relative flex w-full flex-col',
109
+ disabled && 'pointer-events-none opacity-50',
110
+ )}
111
+ >
97
112
  <div className="absolute top-8 left-2 flex items-center">
98
113
  <CurrencyImage price$={$price} />
99
114
  </div>
100
115
 
101
116
  <div className="[&>label>div>.rounded-xl]:h-9 [&>label>div>.rounded-xl]:rounded-sm [&>label>div>.rounded-xl]:px-2 [&>label]:gap-1">
102
117
  <NumericInput
118
+ ref={inputRef}
103
119
  className="ml-5 w-full text-xs"
104
120
  name="price-input"
105
121
  decimals={currencyDecimals}
@@ -18,6 +18,7 @@ type QuantityInputProps = {
18
18
  decimals: number;
19
19
  maxQuantity: string;
20
20
  className?: string;
21
+ disabled?: boolean;
21
22
  };
22
23
 
23
24
  export default observer(function QuantityInput({
@@ -26,6 +27,7 @@ export default observer(function QuantityInput({
26
27
  decimals,
27
28
  maxQuantity,
28
29
  className,
30
+ disabled,
29
31
  }: QuantityInputProps) {
30
32
  const dnMaxQuantity = dn.from(maxQuantity, decimals);
31
33
  const dnOne = dn.from('1', decimals);
@@ -124,6 +126,7 @@ export default observer(function QuantityInput({
124
126
  className={cn(
125
127
  'flex w-full flex-col [&>label>div>div:has(:disabled):hover]:opacity-100 [&>label>div>div:has(:disabled)]:opacity-100 [&>label>div>div>input]:text-xs [&>label>div>div]:h-9 [&>label>div>div]:rounded [&>label>div>div]:pr-0 [&>label>div>div]:pl-3 [&>label>div>div]:text-xs [&>label]:gap-[2px]',
126
128
  className,
129
+ disabled && 'pointer-events-none opacity-50',
127
130
  )}
128
131
  >
129
132
  <NumericInput
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import { Button, Skeleton, Spinner } from '@0xsequence/design-system';
4
+
5
+ const ActionButtons = ({
6
+ onCancel,
7
+ onConfirm,
8
+ disabled,
9
+ loading,
10
+ confirmed,
11
+ tokenSymbol,
12
+ }: {
13
+ onCancel: () => void;
14
+ onConfirm: () => void;
15
+ disabled: boolean;
16
+ loading: boolean;
17
+ confirmed: boolean;
18
+ tokenSymbol?: string;
19
+ }) => (
20
+ <div className="mt-4 flex w-full items-center justify-end gap-2">
21
+ <Button
22
+ pending={loading}
23
+ onClick={onCancel}
24
+ label={<div className="flex items-center gap-2">Cancel</div>}
25
+ variant={'ghost'}
26
+ shape="square"
27
+ size="md"
28
+ />
29
+
30
+ <Button
31
+ disabled={disabled}
32
+ pending={loading || confirmed}
33
+ onClick={onConfirm}
34
+ label={
35
+ <div className="flex items-center gap-2">
36
+ {!confirmed ? (
37
+ tokenSymbol ? (
38
+ `Continue with ${tokenSymbol}`
39
+ ) : (
40
+ <div className="flex items-center gap-2">
41
+ Continue with
42
+ <Skeleton className="h-[20px] w-6 animate-shimmer" />
43
+ </div>
44
+ )
45
+ ) : (
46
+ <div className="flex items-center gap-2">
47
+ <Spinner size="sm" />
48
+ Confirming
49
+ </div>
50
+ )}
51
+ </div>
52
+ }
53
+ variant={'primary'}
54
+ shape="square"
55
+ size="md"
56
+ />
57
+ </div>
58
+ );
59
+
60
+ export default ActionButtons;
@@ -0,0 +1,30 @@
1
+ import { Text, WarningIcon } from '@0xsequence/design-system';
2
+ import { CheckmarkIcon } from '@0xsequence/design-system';
3
+
4
+ const BalanceIndicator = ({
5
+ insufficientBalance,
6
+ currencyBalance,
7
+ selectedFeeOption,
8
+ }: {
9
+ insufficientBalance: boolean;
10
+ currencyBalance?: { formatted: string };
11
+ selectedFeeOption?: { token: { symbol: string } };
12
+ }) => (
13
+ <div className="flex items-center gap-2">
14
+ {insufficientBalance ? (
15
+ <WarningIcon className="text-negative" size="xs" />
16
+ ) : (
17
+ <CheckmarkIcon className="text-positive" size="xs" />
18
+ )}
19
+
20
+ <Text
21
+ className="font-body font-medium text-xs"
22
+ color={insufficientBalance ? 'negative' : 'text100'}
23
+ >
24
+ You have {currencyBalance?.formatted || '0'}{' '}
25
+ {selectedFeeOption?.token.symbol}
26
+ </Text>
27
+ </div>
28
+ );
29
+
30
+ export default BalanceIndicator;
@@ -0,0 +1,126 @@
1
+ 'use client';
2
+
3
+ import { getNetwork } from '@0xsequence/connect';
4
+ import { Divider, Skeleton, Text } from '@0xsequence/design-system';
5
+ import { NetworkType } from '@0xsequence/network';
6
+ import { observer } from '@legendapp/state/react';
7
+ import type { FeeOption } from '../../../../../../types/waas-types';
8
+ import { cn } from '../../../../../../utils';
9
+ import WaasFeeOptionsSelect from '../waasFeeOptionsSelect/WaasFeeOptionsSelect';
10
+ import ActionButtons from './_components/ActionButtons';
11
+ import BalanceIndicator from './_components/BalanceIndicator';
12
+ import { selectWaasFeeOptions$ } from './store';
13
+ import useWaasFeeOptionManager from './useWaasFeeOptionManager';
14
+
15
+ type SelectWaasFeeOptionsProps = {
16
+ onCancel?: () => void;
17
+ chainId: number;
18
+ titleOnConfirm?: string;
19
+ className?: string;
20
+ };
21
+
22
+ const SelectWaasFeeOptions = observer(
23
+ ({
24
+ chainId,
25
+ onCancel,
26
+ titleOnConfirm,
27
+ className,
28
+ }: SelectWaasFeeOptionsProps) => {
29
+ const network = getNetwork(chainId);
30
+ const isTestnet = network.type === NetworkType.TESTNET;
31
+ const {
32
+ selectedFeeOption$,
33
+ selectedFeeOption,
34
+ pendingFeeOptionConfirmation,
35
+ currencyBalance,
36
+ currencyBalanceLoading,
37
+ insufficientBalance,
38
+ feeOptionsConfirmed,
39
+ handleConfirmFeeOption,
40
+ } = useWaasFeeOptionManager(chainId);
41
+
42
+ console.log('pendingFeeOptionConfirmation', pendingFeeOptionConfirmation);
43
+
44
+ const handleCancelFeeOption = () => {
45
+ selectWaasFeeOptions$.hide();
46
+
47
+ onCancel?.();
48
+ };
49
+
50
+ if (
51
+ !selectWaasFeeOptions$.isVisible.get() ||
52
+ isTestnet ||
53
+ !selectedFeeOption
54
+ ) {
55
+ return null;
56
+ }
57
+
58
+ return (
59
+ <div
60
+ className={cn(
61
+ 'flex w-full flex-col gap-2 rounded-2xl bg-button-emphasis p-0 backdrop-blur-md',
62
+ className,
63
+ )}
64
+ >
65
+ <Divider className="mt-0 mb-4" />
66
+
67
+ <Text className="mb-2 font-body text-large" fontWeight="bold">
68
+ {feeOptionsConfirmed ? titleOnConfirm : 'Select a fee option'}
69
+ </Text>
70
+
71
+ {!feeOptionsConfirmed && !pendingFeeOptionConfirmation && (
72
+ <Skeleton className="h-[52px] w-full animate-shimmer rounded-xl" />
73
+ )}
74
+
75
+ {(feeOptionsConfirmed || pendingFeeOptionConfirmation) && (
76
+ <div
77
+ className={cn(
78
+ '[&>label>button>span]:overflow-hidden [&>label>button]:w-full [&>label>button]:text-xs [&>label]:flex [&>label]:w-full',
79
+ feeOptionsConfirmed && 'pointer-events-none opacity-70',
80
+ )}
81
+ >
82
+ <WaasFeeOptionsSelect
83
+ options={
84
+ (pendingFeeOptionConfirmation?.options as FeeOption[]) || [
85
+ selectedFeeOption,
86
+ ]
87
+ }
88
+ selectedFeeOption$={selectedFeeOption$}
89
+ />
90
+ </div>
91
+ )}
92
+
93
+ <div className="flex w-full items-start justify-between">
94
+ {!feeOptionsConfirmed &&
95
+ (!pendingFeeOptionConfirmation || currencyBalanceLoading) && (
96
+ <Skeleton className="h-[20px] w-2/3 animate-shimmer rounded-xl" />
97
+ )}
98
+
99
+ {(feeOptionsConfirmed ||
100
+ (pendingFeeOptionConfirmation && !currencyBalanceLoading)) && (
101
+ <BalanceIndicator
102
+ insufficientBalance={insufficientBalance}
103
+ currencyBalance={currencyBalance}
104
+ selectedFeeOption={selectedFeeOption}
105
+ />
106
+ )}
107
+ </div>
108
+
109
+ <ActionButtons
110
+ onCancel={handleCancelFeeOption}
111
+ onConfirm={handleConfirmFeeOption}
112
+ disabled={
113
+ !selectedFeeOption?.token ||
114
+ insufficientBalance ||
115
+ currencyBalanceLoading
116
+ }
117
+ loading={currencyBalanceLoading}
118
+ confirmed={feeOptionsConfirmed}
119
+ tokenSymbol={selectedFeeOption?.token.symbol}
120
+ />
121
+ </div>
122
+ );
123
+ },
124
+ );
125
+
126
+ export default SelectWaasFeeOptions;
@@ -0,0 +1,25 @@
1
+ import { observable } from '@legendapp/state';
2
+ import type {
3
+ FeeOption,
4
+ WaasFeeOptionConfirmation,
5
+ } from '../../../../../../types/waas-types';
6
+
7
+ type SelectWaasFeeOptionsState = {
8
+ selectedFeeOption: FeeOption | undefined;
9
+ pendingFeeOptionConfirmation: WaasFeeOptionConfirmation | undefined;
10
+ isVisible: boolean;
11
+ hide: () => void;
12
+ };
13
+
14
+ const initialState = {
15
+ selectedFeeOption: undefined,
16
+ pendingFeeOptionConfirmation: undefined,
17
+ isVisible: false,
18
+ hide: () => {
19
+ selectWaasFeeOptions$.isVisible.set(false);
20
+ selectWaasFeeOptions$.selectedFeeOption.set(undefined);
21
+ selectWaasFeeOptions$.pendingFeeOptionConfirmation.set(undefined);
22
+ },
23
+ } as SelectWaasFeeOptionsState;
24
+
25
+ export const selectWaasFeeOptions$ = observable(initialState);