@0xsequence/marketplace-sdk 0.3.2 → 0.3.4

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 (139) hide show
  1. package/dist/{chunk-NII6JJGH.js → chunk-3CXEYH2I.js} +5 -3
  2. package/dist/chunk-3CXEYH2I.js.map +1 -0
  3. package/dist/{chunk-GJAKQ5Q3.js → chunk-53Q7BNZH.js} +8 -2
  4. package/dist/chunk-53Q7BNZH.js.map +1 -0
  5. package/dist/{chunk-O7UQGT43.js → chunk-6LQST3KZ.js} +553 -283
  6. package/dist/chunk-6LQST3KZ.js.map +1 -0
  7. package/dist/{chunk-GZG2QO64.js → chunk-D3HRXVYJ.js} +72 -21
  8. package/dist/chunk-D3HRXVYJ.js.map +1 -0
  9. package/dist/{chunk-4YU2UPYH.js → chunk-FFCNYF3S.js} +61 -61
  10. package/dist/chunk-FFCNYF3S.js.map +1 -0
  11. package/dist/chunk-G2FYRJMK.js +38 -0
  12. package/dist/chunk-G2FYRJMK.js.map +1 -0
  13. package/dist/{chunk-WA433WAJ.js → chunk-L6GSYPCR.js} +20 -6
  14. package/dist/chunk-L6GSYPCR.js.map +1 -0
  15. package/dist/{chunk-SPW24Y7I.js → chunk-OUZ42I6B.js} +7 -2
  16. package/dist/chunk-OUZ42I6B.js.map +1 -0
  17. package/dist/{chunk-22NLQ3AS.js → chunk-PMDJARYX.js} +1081 -980
  18. package/dist/chunk-PMDJARYX.js.map +1 -0
  19. package/dist/chunk-T2AMWIKD.js +277 -0
  20. package/dist/chunk-T2AMWIKD.js.map +1 -0
  21. package/dist/{chunk-G33554LK.js → chunk-WQCWBXBM.js} +8 -16
  22. package/dist/chunk-WQCWBXBM.js.map +1 -0
  23. package/dist/index.css +40 -0
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +172 -71
  26. package/dist/index.js.map +1 -1
  27. package/dist/{marketplace.gen-BLP7822q.d.ts → marketplace.gen-jdKqutnd.d.ts} +57 -18
  28. package/dist/react/_internal/api/index.d.ts +4 -2
  29. package/dist/react/_internal/api/index.js +8 -3
  30. package/dist/react/_internal/index.d.ts +3 -3
  31. package/dist/react/_internal/index.js +9 -4
  32. package/dist/react/_internal/wagmi/index.js +2 -1
  33. package/dist/react/hooks/index.d.ts +285 -12
  34. package/dist/react/hooks/index.js +14 -5
  35. package/dist/react/index.css +29 -29
  36. package/dist/react/index.css.map +1 -1
  37. package/dist/react/index.d.ts +4 -4
  38. package/dist/react/index.js +17 -8
  39. package/dist/react/ssr/index.js +55 -4
  40. package/dist/react/ssr/index.js.map +1 -1
  41. package/dist/react/ui/components/index.css +13 -13
  42. package/dist/react/ui/components/index.css.map +1 -1
  43. package/dist/react/ui/components/index.d.ts +3 -3
  44. package/dist/react/ui/components/index.js +9 -8
  45. package/dist/react/ui/icons/index.js +1 -1
  46. package/dist/react/ui/icons/index.js.map +1 -1
  47. package/dist/react/ui/index.css +29 -29
  48. package/dist/react/ui/index.css.map +1 -1
  49. package/dist/react/ui/index.d.ts +3 -3
  50. package/dist/react/ui/index.js +9 -8
  51. package/dist/react/ui/modals/_internal/components/actionModal/index.js +3 -2
  52. package/dist/react/ui/styles/index.d.ts +1 -1
  53. package/dist/{services-C9-lvWcC.d.ts → services-C2O-7p_M.d.ts} +2 -2
  54. package/dist/styles/index.css +40 -0
  55. package/dist/styles/index.css.map +1 -1
  56. package/dist/styles/index.d.ts +9 -2
  57. package/dist/styles/index.js +14 -2
  58. package/dist/types/index.d.ts +2 -2
  59. package/dist/types/index.js +3 -2
  60. package/dist/{types-QqXjNuUP.d.ts → types-BlDoGvJV.d.ts} +1 -1
  61. package/dist/utils/index.d.ts +2 -2
  62. package/dist/utils/index.js +2 -1
  63. package/package.json +18 -15
  64. package/src/react/_internal/api/marketplace.gen.ts +85 -20
  65. package/src/react/_internal/api/query-keys.ts +2 -0
  66. package/src/react/_internal/api/services.ts +4 -3
  67. package/src/react/_internal/api/zod-schema.ts +20 -0
  68. package/src/react/_internal/transaction-machine/execute-transaction.ts +311 -224
  69. package/src/react/_internal/transaction-machine/logger.ts +66 -0
  70. package/src/react/_internal/transaction-machine/useTransactionMachine.ts +69 -19
  71. package/src/react/_internal/wagmi/embedded.ts +2 -2
  72. package/src/react/hooks/index.ts +2 -0
  73. package/src/react/hooks/options/marketplaceConfigOptions.ts +6 -2
  74. package/src/react/hooks/useBuyCollectable.tsx +14 -8
  75. package/src/react/hooks/useCancelOrder.tsx +4 -3
  76. package/src/react/hooks/useCheckoutOptions.tsx +2 -2
  77. package/src/react/hooks/useConfig.tsx +2 -3
  78. package/src/react/hooks/useCountListingsForCollectible.tsx +64 -0
  79. package/src/react/hooks/useCountOffersForCollectible.tsx +64 -0
  80. package/src/react/hooks/useCreateListing.tsx +13 -14
  81. package/src/react/hooks/useCurrencies.tsx +9 -8
  82. package/src/react/hooks/useCurrency.tsx +6 -6
  83. package/src/react/hooks/useGenerateBuyTransaction.tsx +3 -3
  84. package/src/react/hooks/useMakeOffer.tsx +14 -13
  85. package/src/react/hooks/useRoyaltyPercentage.tsx +1 -1
  86. package/src/react/hooks/useSell.tsx +12 -12
  87. package/src/react/hooks/useTransferTokens.tsx +2 -1
  88. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +3 -2
  89. package/src/react/ui/components/_internals/custom-select/CustomSelect.tsx +4 -4
  90. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +2 -2
  91. package/src/react/ui/components/collectible-card/Footer.tsx +33 -29
  92. package/src/react/ui/icons/ArrowUp.tsx +1 -1
  93. package/src/react/ui/modals/BuyModal/_store.ts +8 -6
  94. package/src/react/ui/modals/BuyModal/index.tsx +120 -81
  95. package/src/react/ui/modals/CreateListingModal/_store.ts +1 -0
  96. package/src/react/ui/modals/CreateListingModal/index.tsx +34 -14
  97. package/src/react/ui/modals/MakeOfferModal/_store.ts +2 -0
  98. package/src/react/ui/modals/MakeOfferModal/index.tsx +21 -11
  99. package/src/react/ui/modals/SellModal/index.tsx +21 -13
  100. package/src/react/ui/modals/SuccessfulPurchaseModal/_store.ts +1 -1
  101. package/src/react/ui/modals/SuccessfulPurchaseModal/index.tsx +1 -1
  102. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/index.tsx +12 -7
  103. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/useHandleTransfer.tsx +2 -1
  104. package/src/react/ui/modals/TransferModal/_views/followWalletInstructions/index.tsx +2 -2
  105. package/src/react/ui/modals/TransferModal/styles.css.ts +1 -1
  106. package/src/react/ui/modals/_internal/components/actionModal/ErrorModal.tsx +1 -1
  107. package/src/react/ui/modals/_internal/components/actionModal/LoadingModal.tsx +1 -1
  108. package/src/react/ui/modals/_internal/components/alertMessage/index.tsx +7 -2
  109. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +2 -7
  110. package/src/react/ui/modals/_internal/components/expirationDateSelect/index.tsx +2 -1
  111. package/src/react/ui/modals/_internal/components/floorPriceText/index.tsx +1 -0
  112. package/src/react/ui/modals/_internal/components/quantityInput/index.tsx +42 -102
  113. package/src/react/ui/modals/_internal/components/switchChainModal/styles.css.ts +1 -1
  114. package/src/react/ui/modals/_internal/components/timeAgo/index.tsx +1 -1
  115. package/src/react/ui/modals/_internal/components/tokenPreview/index.tsx +1 -1
  116. package/src/react/ui/modals/_internal/components/transaction-footer/index.tsx +32 -11
  117. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +2 -2
  118. package/src/react/ui/modals/_internal/components/transactionHeader/index.tsx +14 -2
  119. package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +17 -5
  120. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +12 -4
  121. package/src/react/ui/modals/_internal/components/transactionStatusModal/styles.css.ts +1 -1
  122. package/src/react/ui/modals/modal-provider.tsx +3 -3
  123. package/src/styles/index.ts +3 -0
  124. package/src/utils/_internal/error/base.ts +32 -0
  125. package/src/utils/_internal/error/context.ts +13 -0
  126. package/src/utils/_internal/error/transaction.ts +369 -0
  127. package/src/utils/get-public-rpc-client.ts +4 -1
  128. package/tsconfig.tsbuildinfo +1 -1
  129. package/tsup.config.ts +3 -0
  130. package/dist/chess-tile-6BS5MQT5.png +0 -0
  131. package/dist/chunk-22NLQ3AS.js.map +0 -1
  132. package/dist/chunk-4YU2UPYH.js.map +0 -1
  133. package/dist/chunk-G33554LK.js.map +0 -1
  134. package/dist/chunk-GJAKQ5Q3.js.map +0 -1
  135. package/dist/chunk-GZG2QO64.js.map +0 -1
  136. package/dist/chunk-NII6JJGH.js.map +0 -1
  137. package/dist/chunk-O7UQGT43.js.map +0 -1
  138. package/dist/chunk-SPW24Y7I.js.map +0 -1
  139. package/dist/chunk-WA433WAJ.js.map +0 -1
@@ -1,18 +1,19 @@
1
- import { useState, useCallback } from 'react';
1
+ import { useCallback, useState } from 'react';
2
2
  import type { Hash } from 'viem';
3
+ import type { TransactionError } from '../../utils/_internal/error/transaction';
3
4
  import {
4
5
  type OfferInput,
5
- TransactionType,
6
6
  type TransactionSteps,
7
+ TransactionType,
7
8
  } from '../_internal/transaction-machine/execute-transaction';
8
9
  import {
9
- useTransactionMachine,
10
10
  type UseTransactionMachineConfig,
11
+ useTransactionMachine,
11
12
  } from '../_internal/transaction-machine/useTransactionMachine';
12
13
 
13
14
  interface UseMakeOfferArgs extends Omit<UseTransactionMachineConfig, 'type'> {
14
15
  onSuccess?: (hash: Hash) => void;
15
- onError?: (error: Error) => void;
16
+ onError?: (error: TransactionError) => void;
16
17
  onTransactionSent?: (hash: Hash) => void;
17
18
  }
18
19
 
@@ -25,7 +26,7 @@ export const useMakeOffer = ({
25
26
  const [isLoading, setIsLoading] = useState(false);
26
27
  const [steps, setSteps] = useState<TransactionSteps | null>(null);
27
28
 
28
- const machine = useTransactionMachine(
29
+ const { machine, isLoading: isMachineLoading } = useTransactionMachine(
29
30
  {
30
31
  ...config,
31
32
  type: TransactionType.OFFER,
@@ -39,24 +40,24 @@ export const useMakeOffer = ({
39
40
  async (props: OfferInput) => {
40
41
  if (!machine) return;
41
42
  setIsLoading(true);
42
- try {
43
- const generatedSteps = await machine.getTransactionSteps(props);
44
- setSteps(generatedSteps);
45
- } catch (error) {
46
- onError?.(error as Error);
47
- } finally {
43
+ const generatedSteps = await machine.getTransactionSteps(props);
44
+ if (!generatedSteps) {
48
45
  setIsLoading(false);
46
+ return;
49
47
  }
48
+ setSteps(generatedSteps);
49
+ setIsLoading(false);
50
50
  },
51
51
  [machine, onError],
52
52
  );
53
53
 
54
54
  return {
55
- makeOffer: (props: OfferInput) => machine?.start({ props }),
55
+ makeOffer: (props: OfferInput) => machine?.start(props),
56
56
  getMakeOfferSteps: (props: OfferInput) => ({
57
- isLoading,
57
+ isLoading: isLoading,
58
58
  steps,
59
59
  refreshSteps: () => loadSteps(props),
60
60
  }),
61
+ isLoading: isMachineLoading,
61
62
  };
62
63
  };
@@ -2,6 +2,7 @@ import { queryOptions, useQuery } from '@tanstack/react-query';
2
2
  import type { Hex } from 'viem';
3
3
  import { getContract } from 'viem';
4
4
  import { z } from 'zod';
5
+ import { EIP2981_ABI } from '../../utils';
5
6
  import { getPublicRpcClient } from '../../utils/get-public-rpc-client';
6
7
  import {
7
8
  AddressSchema,
@@ -9,7 +10,6 @@ import {
9
10
  QueryArgSchema,
10
11
  collectableKeys,
11
12
  } from '../_internal';
12
- import { EIP2981_ABI } from '../../utils';
13
13
 
14
14
  const UseRoyaletyPercentageSchema = z.object({
15
15
  chainId: ChainIdSchema.pipe(z.coerce.string()),
@@ -1,13 +1,13 @@
1
- import { useState, useCallback } from 'react';
1
+ import { useCallback, useState } from 'react';
2
2
  import type { Hash } from 'viem';
3
3
  import {
4
4
  type SellInput,
5
- TransactionType,
6
5
  type TransactionSteps,
6
+ TransactionType,
7
7
  } from '../_internal/transaction-machine/execute-transaction';
8
8
  import {
9
- useTransactionMachine,
10
9
  type UseTransactionMachineConfig,
10
+ useTransactionMachine,
11
11
  } from '../_internal/transaction-machine/useTransactionMachine';
12
12
 
13
13
  interface UseSellArgs extends Omit<UseTransactionMachineConfig, 'type'> {
@@ -25,7 +25,7 @@ export const useSell = ({
25
25
  const [isLoading, setIsLoading] = useState(false);
26
26
  const [steps, setSteps] = useState<TransactionSteps | null>(null);
27
27
 
28
- const machine = useTransactionMachine(
28
+ const { machine, isLoading: isMachineLoading } = useTransactionMachine(
29
29
  {
30
30
  ...config,
31
31
  type: TransactionType.SELL,
@@ -39,24 +39,24 @@ export const useSell = ({
39
39
  async (props: SellInput) => {
40
40
  if (!machine) return;
41
41
  setIsLoading(true);
42
- try {
43
- const generatedSteps = await machine.getTransactionSteps(props);
44
- setSteps(generatedSteps);
45
- } catch (error) {
46
- onError?.(error as Error);
47
- } finally {
42
+ const generatedSteps = await machine.getTransactionSteps(props);
43
+ if (!generatedSteps) {
48
44
  setIsLoading(false);
45
+ return;
49
46
  }
47
+ setSteps(generatedSteps);
48
+ setIsLoading(false);
50
49
  },
51
50
  [machine, onError],
52
51
  );
53
52
 
54
53
  return {
55
- sell: (props: SellInput) => machine?.start({ props }),
54
+ sell: (props: SellInput) => machine?.start(props),
56
55
  getSellSteps: (props: SellInput) => ({
57
- isLoading,
56
+ isLoading: isLoading,
58
57
  steps,
59
58
  refreshSteps: () => loadSteps(props),
60
59
  }),
60
+ isLoading: isMachineLoading,
61
61
  };
62
62
  };
@@ -1,6 +1,7 @@
1
1
  import { type Abi, type Address, type Hex, erc721Abi } from 'viem';
2
2
  import { useAccount, useWriteContract } from 'wagmi';
3
3
  import { ERC1155_ABI } from '../../utils';
4
+ import { NoWalletConnectedError } from '../../utils/_internal/error/transaction';
4
5
  import type { ChainId, ContractType } from '../_internal';
5
6
 
6
7
  interface BaseTransferParams {
@@ -64,7 +65,7 @@ export const useTransferTokens = () => {
64
65
 
65
66
  const transferTokensAsync = async (params: TransferTokensParams) => {
66
67
  if (!accountAddress) {
67
- throw new Error('No wallet connected');
68
+ throw new NoWalletConnectedError();
68
69
  }
69
70
 
70
71
  const config = prepareTransferConfig(params, accountAddress);
@@ -3,6 +3,7 @@
3
3
  import { Button } from '@0xsequence/design-system';
4
4
  import { observer } from '@legendapp/state/react';
5
5
  import type { Hex } from 'viem';
6
+ import { InvalidStepError } from '../../../../../utils/_internal/error/transaction';
6
7
  import type { Order } from '../../../../_internal';
7
8
  import { useBuyModal } from '../../../modals/BuyModal';
8
9
  import { useCreateListingModal } from '../../../modals/CreateListingModal';
@@ -46,7 +47,7 @@ export const ActionButton = observer(
46
47
 
47
48
  if (action === CollectibleCardAction.BUY) {
48
49
  if (!lowestListing)
49
- throw new Error('lowestListing is required for BUY action');
50
+ throw new InvalidStepError('BUY', 'lowestListing is required');
50
51
 
51
52
  return (
52
53
  <ActionButtonBody
@@ -65,7 +66,7 @@ export const ActionButton = observer(
65
66
 
66
67
  if (action === CollectibleCardAction.SELL) {
67
68
  if (!highestOffer)
68
- throw new Error('highestOffer is required for SELL action');
69
+ throw new InvalidStepError('SELL', 'highestOffer is required');
69
70
 
70
71
  return (
71
72
  <ActionButtonBody
@@ -1,6 +1,6 @@
1
- import React from 'react';
2
- import * as Select from '@radix-ui/react-select';
3
1
  import { CheckmarkIcon, ChevronDownIcon } from '@0xsequence/design-system';
2
+ import * as Select from '@radix-ui/react-select';
3
+ import React from 'react';
4
4
  import { content, item, itemIndicator, trigger } from './styles.css';
5
5
 
6
6
  interface CustomSelectProps {
@@ -46,9 +46,9 @@ export const CustomSelect: React.FC<CustomSelectProps> = ({
46
46
  <Select.Portal>
47
47
  <Select.Content className={content}>
48
48
  <Select.Viewport>
49
- {items.map((item) => (
49
+ {items.map((item, index) => (
50
50
  <CustomSelectItem
51
- key={item.value}
51
+ key={index}
52
52
  value={item.value}
53
53
  disabled={item.disabled}
54
54
  >
@@ -59,7 +59,7 @@ type CollectibleCardProps = {
59
59
  collectionAddress: Hex;
60
60
  collectionType?: ContractType;
61
61
  lowestListing: CollectibleOrder | undefined;
62
- onCollectibleClick?: () => void;
62
+ onCollectibleClick?: (tokenId: string) => void;
63
63
  onOfferClick?: ({ order }: { order?: Order }) => void;
64
64
  balance?: string;
65
65
  cardLoading?: boolean;
@@ -123,7 +123,7 @@ export function CollectibleCard({
123
123
  height="full"
124
124
  zIndex="10"
125
125
  overflow="hidden"
126
- onClick={onCollectibleClick}
126
+ onClick={() => onCollectibleClick?.(collectibleId)}
127
127
  border="none"
128
128
  cursor="pointer"
129
129
  padding="0"
@@ -51,6 +51,7 @@ export const Footer = ({
51
51
  alignItems="center"
52
52
  justifyContent="space-between"
53
53
  position="relative"
54
+ width="full"
54
55
  >
55
56
  <Text
56
57
  color="text100"
@@ -64,8 +65,12 @@ export const Footer = ({
64
65
 
65
66
  {highestOffer && onOfferClick && (
66
67
  <IconButton
68
+ size="xs"
67
69
  variant="primary"
68
70
  className={offerBellButton}
71
+ position="absolute"
72
+ right="0"
73
+ top="0"
69
74
  onClick={(e) => {
70
75
  e.stopPropagation();
71
76
  onOfferClick?.();
@@ -94,35 +99,34 @@ export const Footer = ({
94
99
  </Box>
95
100
  )}
96
101
 
97
- {!!balance && type !== ContractType.ERC721 && (
98
- <Text
99
- background="backgroundSecondary"
100
- color="text80"
101
- fontSize="small"
102
- textAlign="left"
103
- fontFamily="body"
104
- paddingX="2"
105
- paddingY="1"
106
- borderRadius="sm"
107
- >
108
- Owned: {balance}
109
- </Text>
110
- )}
111
-
112
- {type === ContractType.ERC721 && (
113
- <Text
114
- background="backgroundSecondary"
115
- color="text80"
116
- fontSize="small"
117
- textAlign="left"
118
- fontFamily="body"
119
- paddingX="2"
120
- paddingY="1"
121
- borderRadius="sm"
122
- >
123
- ERC-721
124
- </Text>
125
- )}
102
+ <TokenTypeBalancePill balance={balance} type={type as ContractType} />
126
103
  </Box>
127
104
  );
128
105
  };
106
+
107
+ const TokenTypeBalancePill = ({
108
+ balance,
109
+ type,
110
+ }: { balance?: string; type: ContractType }) => {
111
+ const displayText =
112
+ type === ContractType.ERC1155
113
+ ? !!balance
114
+ ? `Owned: ${balance}`
115
+ : 'ERC-1155'
116
+ : 'ERC-721';
117
+
118
+ return (
119
+ <Text
120
+ background="backgroundSecondary"
121
+ color="text80"
122
+ fontSize="small"
123
+ textAlign="left"
124
+ fontFamily="body"
125
+ paddingX="2"
126
+ paddingY="1"
127
+ borderRadius="sm"
128
+ >
129
+ {displayText}
130
+ </Text>
131
+ );
132
+ };
@@ -1,5 +1,5 @@
1
- import { iconVariants } from './styles.css';
2
1
  import { Box, type IconProps } from '@0xsequence/design-system';
2
+ import { iconVariants } from './styles.css';
3
3
 
4
4
  const Svg = () => (
5
5
  <svg
@@ -1,6 +1,6 @@
1
1
  import { observable } from '@legendapp/state';
2
- import type { Order } from '../../../_internal';
3
2
  import type { ShowBuyModalArgs } from '.';
3
+ import type { Order } from '../../../_internal';
4
4
  import type { ModalCallbacks } from '../_internal/types';
5
5
 
6
6
  export interface BuyModalState {
@@ -15,6 +15,8 @@ export interface BuyModalState {
15
15
  state: {
16
16
  order: Order;
17
17
  quantity: string;
18
+ modalId: number;
19
+ invalidQuantity: boolean;
18
20
  };
19
21
  callbacks?: ModalCallbacks;
20
22
  }
@@ -30,22 +32,22 @@ export const initialState: BuyModalState = {
30
32
  defaultCallbacks?: ModalCallbacks;
31
33
  }) => {
32
34
  buyModal$.state.set({
33
- ...buyModal$.state.get(),
35
+ quantity: args.order.quantityAvailableFormatted,
34
36
  order: args.order,
37
+ modalId: buyModal$.state.modalId.get() + 1,
38
+ invalidQuantity: false,
35
39
  });
36
40
  buyModal$.callbacks.set(callbacks || defaultCallbacks);
37
41
  buyModal$.isOpen.set(true);
38
42
  },
39
43
  close: () => {
40
44
  buyModal$.isOpen.set(false);
41
- buyModal$.callbacks.set(undefined);
42
- buyModal$.state.set({
43
- ...initialState.state,
44
- });
45
45
  },
46
46
  state: {
47
47
  order: undefined as unknown as Order,
48
48
  quantity: '1',
49
+ modalId: 0,
50
+ invalidQuantity: false,
49
51
  },
50
52
  callbacks: undefined,
51
53
  };
@@ -1,13 +1,15 @@
1
+ import type { TokenMetadata } from '@0xsequence/indexer';
2
+ import { Show, observer, useSelector } from '@legendapp/state/react';
3
+ import { useEffect } from 'react';
1
4
  import type { Hex } from 'viem';
2
- import { buyModal$ } from './_store';
3
5
  import { ContractType, type Order } from '../../../_internal';
4
- import { observer, useSelector } from '@legendapp/state/react';
6
+ import type { BuyInput } from '../../../_internal/transaction-machine/execute-transaction';
5
7
  import { useCollectible, useCollection } from '../../../hooks';
6
- import { ActionModal } from '../_internal/components/actionModal';
7
- import { useEffect } from 'react';
8
- import QuantityInput from '..//_internal/components/quantityInput';
9
8
  import { useBuyCollectable } from '../../../hooks/useBuyCollectable';
9
+ import QuantityInput from '..//_internal/components/quantityInput';
10
+ import { ActionModal } from '../_internal/components/actionModal';
10
11
  import type { ModalCallbacks } from '../_internal/types';
12
+ import { buyModal$ } from './_store';
11
13
 
12
14
  export type ShowBuyModalArgs = {
13
15
  chainId: string;
@@ -24,62 +26,33 @@ export const useBuyModal = (callbacks?: ModalCallbacks) => {
24
26
  };
25
27
  };
26
28
 
27
- export const BuyModal = () => {
28
- const isOpen = useSelector(buyModal$.isOpen);
29
- const { data: collection } = useCollection({
30
- chainId: buyModal$.state.order.chainId.get(),
31
- collectionAddress: buyModal$.state.order.collectionContractAddress.get(),
32
- });
33
-
34
- if (!isOpen || !collection) return null;
35
-
36
- return collection.type === ContractType.ERC721 ? (
37
- <CheckoutModal />
38
- ) : (
39
- <ERC1155QuantityModal />
40
- );
41
- };
42
-
43
- const CheckoutModal = observer(() => {
44
- const order = buyModal$.state.order.get();
45
- const chainId = String(order.chainId);
46
- const collectionAddress = order.collectionContractAddress as Hex;
47
- const collectibleId = order.tokenId;
29
+ export const BuyModal = () => (
30
+ <Show if={buyModal$.isOpen}>
31
+ <BuyModalContent />
32
+ </Show>
33
+ );
48
34
 
49
- const { buy } = useBuyCollectable({
50
- chainId,
51
- collectionAddress,
52
- });
35
+ export const BuyModalContent = () => {
36
+ const chainId = String(useSelector(buyModal$.state.order.chainId));
37
+ const collectionAddress = useSelector(
38
+ buyModal$.state.order.collectionContractAddress,
39
+ ) as Hex;
40
+ const collectibleId = useSelector(buyModal$.state.order.tokenId);
41
+ const modalId = useSelector(buyModal$.state.modalId);
53
42
 
54
- const { data: collectable } = useCollectible({
43
+ const { data: collection } = useCollection({
55
44
  chainId,
56
45
  collectionAddress,
57
- collectibleId,
58
46
  });
59
47
 
60
- // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
61
- useEffect(() => {
62
- if (!collectable) return;
63
- buy({
64
- orderId: order.orderId,
65
- collectableDecimals: collectable.decimals || 0,
66
- quantity: '1',
67
- marketplace: order.marketplace,
68
- });
69
- }, [order, collectable]);
70
-
71
- return <></>;
72
- });
73
-
74
- const ERC1155QuantityModal = observer(() => {
75
- const order = buyModal$.state.order.get();
76
- const chainId = String(order.chainId);
77
- const collectionAddress = order.collectionContractAddress as Hex;
78
- const collectibleId = order.tokenId;
79
-
80
- const { buy } = useBuyCollectable({
48
+ const { buy, isLoading } = useBuyCollectable({
81
49
  chainId,
82
50
  collectionAddress,
51
+ onError: buyModal$.callbacks.get()?.onError,
52
+ onSuccess: (hash) => {
53
+ buyModal$.callbacks.get()?.onSuccess?.(hash);
54
+ buyModal$.close();
55
+ },
83
56
  });
84
57
 
85
58
  const { data: collectable } = useCollectible({
@@ -88,32 +61,98 @@ const ERC1155QuantityModal = observer(() => {
88
61
  collectibleId,
89
62
  });
90
63
 
91
- if (!order || !collectable) return null;
92
-
93
- return (
94
- <ActionModal
95
- store={buyModal$}
96
- onClose={() => buyModal$.close()}
97
- title="Select Quantity"
98
- ctas={[
99
- {
100
- label: 'Select Quantity',
101
- onClick: () =>
102
- buy({
103
- quantity: buyModal$.state.quantity.get(),
104
- orderId: order.orderId,
105
- collectableDecimals: collectable.decimals || 0,
106
- marketplace: order.marketplace,
107
- }),
108
- },
109
- ]}
110
- >
111
- <QuantityInput
112
- chainId={chainId}
113
- collectionAddress={collectionAddress}
114
- collectibleId={collectibleId}
115
- $quantity={buyModal$.state.quantity}
116
- />
117
- </ActionModal>
64
+ if (modalId == 0 || !collection || !collectable || !buy) return null;
65
+
66
+ return collection.type === ContractType.ERC721 ? (
67
+ <CheckoutModal
68
+ key={modalId}
69
+ buy={buy}
70
+ collectable={collectable}
71
+ order={buyModal$.state.order.get()}
72
+ isLoading={isLoading}
73
+ />
74
+ ) : (
75
+ <ERC1155QuantityModal
76
+ buy={buy}
77
+ collectable={collectable}
78
+ order={buyModal$.state.order.get()}
79
+ chainId={chainId}
80
+ collectionAddress={collectionAddress}
81
+ collectibleId={collectibleId}
82
+ isLoading={isLoading}
83
+ />
118
84
  );
119
- });
85
+ };
86
+
87
+ interface CheckoutModalProps {
88
+ buy: (props: BuyInput) => void;
89
+ collectable: TokenMetadata;
90
+ order: Order;
91
+ isLoading?: boolean;
92
+ }
93
+
94
+ function CheckoutModal({
95
+ buy,
96
+ collectable,
97
+ order,
98
+ isLoading,
99
+ }: CheckoutModalProps) {
100
+ useEffect(() => {
101
+ const executeBuy = () => {
102
+ if (isLoading) return;
103
+ buy({
104
+ orderId: order.orderId,
105
+ collectableDecimals: collectable.decimals || 0,
106
+ quantity: '1',
107
+ marketplace: order.marketplace,
108
+ });
109
+ };
110
+
111
+ executeBuy();
112
+ }, [isLoading]);
113
+
114
+ return null;
115
+ }
116
+
117
+ interface ERC1155QuantityModalProps extends CheckoutModalProps {
118
+ chainId: string;
119
+ collectionAddress: Hex;
120
+ collectibleId: string;
121
+ }
122
+
123
+ const ERC1155QuantityModal = observer(
124
+ ({ buy, collectable, order }: ERC1155QuantityModalProps) => {
125
+ buyModal$.state.quantity.set(
126
+ Math.max(Number(order.quantityRemaining), 1).toString(),
127
+ );
128
+
129
+ return (
130
+ <ActionModal
131
+ store={buyModal$}
132
+ onClose={() => buyModal$.close()}
133
+ title="Select Quantity"
134
+ ctas={[
135
+ {
136
+ label: 'Select Quantity',
137
+ onClick: () => {
138
+ buy({
139
+ quantity: buyModal$.state.quantity.get(),
140
+ orderId: order.orderId,
141
+ collectableDecimals: collectable.decimals || 0,
142
+ marketplace: order.marketplace,
143
+ });
144
+ buyModal$.close();
145
+ },
146
+ },
147
+ ]}
148
+ >
149
+ <QuantityInput
150
+ $quantity={buyModal$.state.quantity}
151
+ $invalidQuantity={buyModal$.state.invalidQuantity}
152
+ decimals={order.quantityDecimals}
153
+ maxQuantity={order.quantityRemaining}
154
+ />
155
+ </ActionModal>
156
+ );
157
+ },
158
+ );
@@ -16,6 +16,7 @@ const initialState = {
16
16
  currency: {} as Currency,
17
17
  },
18
18
  quantity: '1',
19
+ invalidQuantity: false,
19
20
  expiry: new Date(addDays(new Date(), 7).toJSON()),
20
21
  callbacks: undefined as ModalCallbacks | undefined,
21
22