@0xsequence/marketplace-sdk 0.3.11 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/{chunk-3LOJQCTV.js → chunk-5GDO4ZBC.js} +5 -4
  2. package/dist/{chunk-3LOJQCTV.js.map → chunk-5GDO4ZBC.js.map} +1 -1
  3. package/dist/{chunk-E3ZFT6WR.js → chunk-EVRILXOH.js} +12 -2
  4. package/dist/chunk-EVRILXOH.js.map +1 -0
  5. package/dist/{chunk-N5F6IEAK.js → chunk-GSDUAHL3.js} +1 -1
  6. package/dist/chunk-GSDUAHL3.js.map +1 -0
  7. package/dist/{chunk-IRN77TJW.js → chunk-IOTKCWOB.js} +925 -667
  8. package/dist/chunk-IOTKCWOB.js.map +1 -0
  9. package/dist/{chunk-KRMV6FJE.js → chunk-KNX2LER4.js} +5 -6
  10. package/dist/{chunk-4FC3IG7C.js.map → chunk-KNX2LER4.js.map} +1 -1
  11. package/dist/{chunk-SBVLWSRZ.js → chunk-LF44FCG5.js} +2 -2
  12. package/dist/{chunk-SBVLWSRZ.js.map → chunk-LF44FCG5.js.map} +1 -1
  13. package/dist/{chunk-4FC3IG7C.js → chunk-LSMQVX77.js} +5 -6
  14. package/dist/{chunk-KRMV6FJE.js.map → chunk-LSMQVX77.js.map} +1 -1
  15. package/dist/{chunk-B3LFJJVS.js → chunk-MIYMMP2K.js} +90 -40
  16. package/dist/chunk-MIYMMP2K.js.map +1 -0
  17. package/dist/{chunk-Z7NLKEXF.js → chunk-QMO2CUNM.js} +2 -2
  18. package/dist/{chunk-L6GSYPCR.js → chunk-RZSZNVEH.js} +5 -5
  19. package/dist/{chunk-L6GSYPCR.js.map → chunk-RZSZNVEH.js.map} +1 -1
  20. package/dist/chunk-T5T6JNB2.js +171 -0
  21. package/dist/chunk-T5T6JNB2.js.map +1 -0
  22. package/dist/chunk-UPLTM63S.js +435 -0
  23. package/dist/chunk-UPLTM63S.js.map +1 -0
  24. package/dist/{chunk-Y7YO5TLE.js → chunk-XXML5K3X.js} +5 -2
  25. package/dist/chunk-XXML5K3X.js.map +1 -0
  26. package/dist/{create-config-CgtmCzvb.d.ts → create-config-8sffBvlt.d.ts} +1 -1
  27. package/dist/index.js +5 -5
  28. package/dist/react/_internal/api/index.js +2 -2
  29. package/dist/react/_internal/index.d.ts +1 -1
  30. package/dist/react/_internal/index.js +3 -3
  31. package/dist/react/_internal/wagmi/index.d.ts +1 -1
  32. package/dist/react/_internal/wagmi/index.js +2 -2
  33. package/dist/react/hooks/index.d.ts +666 -77
  34. package/dist/react/hooks/index.js +8 -8
  35. package/dist/react/index.css +17 -0
  36. package/dist/react/index.css.map +1 -1
  37. package/dist/react/index.d.ts +1 -1
  38. package/dist/react/index.js +12 -12
  39. package/dist/react/ssr/index.js +1 -1
  40. package/dist/react/ssr/index.js.map +1 -1
  41. package/dist/react/ui/components/index.css +17 -0
  42. package/dist/react/ui/components/index.css.map +1 -1
  43. package/dist/react/ui/components/index.js +12 -12
  44. package/dist/react/ui/icons/index.js +4 -4
  45. package/dist/react/ui/icons/index.js.map +1 -1
  46. package/dist/react/ui/index.css +17 -0
  47. package/dist/react/ui/index.css.map +1 -1
  48. package/dist/react/ui/index.js +12 -12
  49. package/dist/react/ui/modals/_internal/components/actionModal/index.css +22 -0
  50. package/dist/react/ui/modals/_internal/components/actionModal/index.css.map +1 -1
  51. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +17 -9
  52. package/dist/react/ui/modals/_internal/components/actionModal/index.js +8 -4
  53. package/dist/react/ui/styles/index.d.ts +1 -1
  54. package/dist/styles/index.d.ts +1 -1
  55. package/dist/styles/index.js +2 -2
  56. package/dist/types/index.js +3 -3
  57. package/dist/utils/index.js +2 -2
  58. package/package.json +25 -25
  59. package/src/react/_internal/api/marketplace-api.ts +3 -2
  60. package/src/react/_internal/transaction-machine/execute-transaction.ts +41 -19
  61. package/src/react/_internal/transaction-machine/useTransactionMachine.ts +43 -8
  62. package/src/react/hooks/useBuyCollectable.tsx +15 -8
  63. package/src/react/hooks/useCancelOrder.tsx +15 -7
  64. package/src/react/hooks/useCreateListing.tsx +75 -11
  65. package/src/react/hooks/useCurrencies.tsx +1 -2
  66. package/src/react/hooks/useCurrencyBalance.tsx +1 -1
  67. package/src/react/hooks/useCurrencyOptions.tsx +1 -1
  68. package/src/react/hooks/useMakeOffer.tsx +74 -12
  69. package/src/react/hooks/useSell.tsx +73 -12
  70. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +1 -7
  71. package/src/react/ui/components/_internals/action-button/types.ts +7 -0
  72. package/src/react/ui/components/_internals/custom-select/CustomSelect.tsx +18 -15
  73. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +5 -7
  74. package/src/react/ui/components/collectible-card/Footer.tsx +5 -7
  75. package/src/react/ui/components/collectible-card/styles.css.ts +1 -1
  76. package/src/react/ui/icons/ArrowUp.tsx +3 -0
  77. package/src/react/ui/icons/Bell.tsx +3 -0
  78. package/src/react/ui/icons/CalendarIcon.tsx +3 -0
  79. package/src/react/ui/icons/DiamondEye.tsx +3 -0
  80. package/src/react/ui/icons/InfoIcon.tsx +3 -0
  81. package/src/react/ui/icons/InventoryIcon.tsx +3 -0
  82. package/src/react/ui/icons/MinusIcon.tsx +3 -0
  83. package/src/react/ui/icons/PlusIcon.tsx +3 -0
  84. package/src/react/ui/icons/PositiveCircleIcon.tsx +3 -0
  85. package/src/react/ui/modals/BuyModal/index.tsx +25 -8
  86. package/src/react/ui/modals/CreateListingModal/_store.ts +5 -2
  87. package/src/react/ui/modals/CreateListingModal/index.tsx +66 -25
  88. package/src/react/ui/modals/MakeOfferModal/_store.ts +5 -2
  89. package/src/react/ui/modals/MakeOfferModal/index.tsx +87 -67
  90. package/src/react/ui/modals/SellModal/index.tsx +107 -57
  91. package/src/react/ui/modals/TransferModal/_store.ts +1 -1
  92. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/index.tsx +4 -2
  93. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/useHandleTransfer.tsx +5 -5
  94. package/src/react/ui/modals/TransferModal/index.tsx +1 -1
  95. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +29 -8
  96. package/src/react/ui/modals/_internal/components/actionModal/ErrorModal.tsx +15 -5
  97. package/src/react/ui/modals/_internal/components/actionModal/LoadingModal.tsx +15 -5
  98. package/src/react/ui/modals/_internal/components/actionModal/store.ts +6 -0
  99. package/src/react/ui/modals/_internal/components/calendar/index.tsx +1 -1
  100. package/src/react/ui/modals/_internal/components/currencyImage/index.tsx +3 -3
  101. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +11 -16
  102. package/src/react/ui/modals/_internal/components/expirationDateSelect/index.tsx +14 -19
  103. package/src/react/ui/modals/_internal/components/priceInput/index.tsx +34 -12
  104. package/src/react/ui/modals/_internal/components/quantityInput/index.tsx +3 -3
  105. package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +7 -4
  106. package/src/react/ui/modals/_internal/components/tokenPreview/index.tsx +1 -0
  107. package/src/react/ui/modals/_internal/components/transaction-footer/index.tsx +10 -8
  108. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +9 -5
  109. package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +7 -4
  110. package/src/react/ui/modals/_internal/components/transactionPreview/useTransactionPreviewTitle.tsx +9 -11
  111. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +60 -31
  112. package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +6 -2
  113. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getFormattedType.ts +1 -1
  114. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getMessage.ts +8 -2
  115. package/src/react/ui/modals/_internal/components/transactionStatusModal/util/getTitle.ts +12 -3
  116. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/index.tsx +146 -0
  117. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/store.ts +12 -0
  118. package/src/react/ui/modals/_internal/components/waasFeeOptionsBox/styles.css.ts +53 -0
  119. package/src/react/ui/modals/_internal/components/waasFeeOptionsSelect/WaasFeeOptionsSelect.tsx +117 -0
  120. package/src/utils/_internal/error/transaction.ts +12 -2
  121. package/src/utils/price.ts +3 -4
  122. package/tsconfig.json +1 -21
  123. package/tsconfig.tsbuildinfo +1 -1
  124. package/dist/chunk-B3LFJJVS.js.map +0 -1
  125. package/dist/chunk-E3ZFT6WR.js.map +0 -1
  126. package/dist/chunk-FFCNYF3S.js +0 -153
  127. package/dist/chunk-FFCNYF3S.js.map +0 -1
  128. package/dist/chunk-IRN77TJW.js.map +0 -1
  129. package/dist/chunk-N5F6IEAK.js.map +0 -1
  130. package/dist/chunk-NMCGA2TB.js +0 -98
  131. package/dist/chunk-NMCGA2TB.js.map +0 -1
  132. package/dist/chunk-Y7YO5TLE.js.map +0 -1
  133. /package/dist/{chunk-Z7NLKEXF.js.map → chunk-QMO2CUNM.js.map} +0 -0
@@ -21,15 +21,31 @@ export type UseTransactionMachineConfig = Omit<
21
21
  'sdkConfig' | 'marketplaceConfig' | 'walletKind' | 'chains' | 'isWaaS'
22
22
  >;
23
23
 
24
- export const useTransactionMachine = (
25
- config: UseTransactionMachineConfig,
26
- onSuccess?: (hash: Hash) => void,
27
- onError?: (error: TransactionError) => void,
28
- onTransactionSent?: (hash: Hash) => void,
29
- ) => {
24
+ export const useTransactionMachine = ({
25
+ config,
26
+ enabled,
27
+ onSuccess,
28
+ onError,
29
+ onTransactionSent,
30
+ onApprovalSuccess,
31
+ onSwitchChainRefused,
32
+ }: {
33
+ config: UseTransactionMachineConfig;
34
+ enabled: boolean;
35
+ onSuccess?: (hash: Hash) => void;
36
+ onError?: (error: TransactionError) => void;
37
+ onTransactionSent?: (
38
+ hash?: Hash,
39
+ orderId?: string,
40
+ isApproval?: boolean,
41
+ ) => void;
42
+ onApprovalSuccess?: (hash: Hash) => void;
43
+ onSwitchChainRefused: () => void;
44
+ }) => {
30
45
  const { data: walletClient, isLoading: walletClientIsLoading } =
31
46
  useWalletClient();
32
- const { show: showSwitchChainModal } = useSwitchChainModal();
47
+ const { show: showSwitchChainModal, close: closeSwitchChainModal } =
48
+ useSwitchChainModal();
33
49
  const sdkConfig = useConfig();
34
50
  const {
35
51
  data: marketplaceConfig,
@@ -39,13 +55,15 @@ export const useTransactionMachine = (
39
55
  const { openSelectPaymentModal } = useSelectPaymentModal();
40
56
  const { chains } = useSwitchChain();
41
57
 
42
- const { connector, isConnected } = useAccount();
58
+ const { connector, isConnected, chainId: accountChainId } = useAccount();
43
59
  const walletKind =
44
60
  connector?.id === 'sequence' ? WalletKind.sequence : WalletKind.unknown;
45
61
 
46
62
  // TODO: remove this once we have a better way to check if the wallet is a WAAS wallet
47
63
  const isWaaS = connector?.id.endsWith('waas') || false;
48
64
 
65
+ if (!enabled) return { machine: null, error: null, isLoading: false };
66
+
49
67
  if (!isConnected) {
50
68
  // No wallet connected, TODO: add some sort of state for this
51
69
  return { machine: null, error: null, isLoading: false };
@@ -75,6 +93,22 @@ export const useTransactionMachine = (
75
93
  return { machine: null, error };
76
94
  }
77
95
 
96
+ if (accountChainId !== Number(config.chainId)) {
97
+ showSwitchChainModal({
98
+ chainIdToSwitchTo: Number(config.chainId),
99
+ onSuccess: () => {
100
+ closeSwitchChainModal();
101
+ },
102
+ onError: (err) => {
103
+ throw err;
104
+ },
105
+ onClose: () => {
106
+ onSwitchChainRefused();
107
+ },
108
+ });
109
+ return { machine: null, error: null, isLoading: false };
110
+ }
111
+
78
112
  const machine = new TransactionMachine(
79
113
  {
80
114
  config: {
@@ -87,6 +121,7 @@ export const useTransactionMachine = (
87
121
  },
88
122
  onSuccess,
89
123
  onTransactionSent,
124
+ onApprovalSuccess,
90
125
  },
91
126
  walletClient,
92
127
  getPublicRpcClient(config.chainId),
@@ -1,4 +1,4 @@
1
- import type { Hash } from 'viem';
1
+ import type { Hash, Hex } from 'viem';
2
2
  import type { TransactionErrorTypes } from '../../utils/_internal/error/transaction';
3
3
  import {
4
4
  type BuyInput,
@@ -15,24 +15,31 @@ interface UseBuyOrderArgs
15
15
  extends Omit<UseTransactionMachineConfig, 'type' | 'orderbookKind'> {
16
16
  onSuccess?: (hash: Hash) => void;
17
17
  onError?: (error: UseBuyOrderError) => void;
18
- onTransactionSent?: (hash: string) => void;
18
+ onTransactionSent?: (hash?: Hex) => void;
19
+ onSwitchChainRefused: () => void;
20
+ enabled: boolean;
19
21
  }
20
22
 
21
23
  export const useBuyCollectable = ({
22
24
  onSuccess,
23
25
  onError,
24
26
  onTransactionSent,
27
+ onSwitchChainRefused,
28
+ enabled,
25
29
  ...config
26
30
  }: UseBuyOrderArgs) => {
27
- const { machine, error, isLoading } = useTransactionMachine(
28
- {
29
- ...config,
30
- type: TransactionType.BUY,
31
- },
31
+ const machineConfig = {
32
+ ...config,
33
+ type: TransactionType.BUY,
34
+ };
35
+ const { machine, error, isLoading } = useTransactionMachine({
36
+ config: machineConfig,
37
+ enabled,
38
+ onSwitchChainRefused,
32
39
  onSuccess,
33
40
  onError,
34
41
  onTransactionSent,
35
- );
42
+ });
36
43
 
37
44
  return {
38
45
  buy: (props: BuyInput) => {
@@ -1,3 +1,4 @@
1
+ import type { Hex } from 'viem';
1
2
  import {
2
3
  type CancelInput,
3
4
  TransactionType,
@@ -11,24 +12,31 @@ interface UseCancelOrderArgs
11
12
  extends Omit<UseTransactionMachineConfig, 'type' | 'orderbookKind'> {
12
13
  onSuccess?: (hash: string) => void;
13
14
  onError?: (error: Error) => void;
14
- onTransactionSent?: (hash: string) => void;
15
+ onTransactionSent?: (hash?: Hex) => void;
16
+ onSwitchChainRefused: () => void;
17
+ enabled: boolean;
15
18
  }
16
19
 
17
20
  export const useCancelOrder = ({
18
21
  onSuccess,
19
22
  onError,
20
23
  onTransactionSent,
24
+ onSwitchChainRefused,
25
+ enabled,
21
26
  ...config
22
27
  }: UseCancelOrderArgs) => {
23
- const { machine, isLoading } = useTransactionMachine(
24
- {
25
- ...config,
26
- type: TransactionType.CANCEL,
27
- },
28
+ const machineConfig = {
29
+ ...config,
30
+ type: TransactionType.CANCEL,
31
+ };
32
+ const { machine, isLoading } = useTransactionMachine({
33
+ config: machineConfig,
34
+ enabled,
35
+ onSwitchChainRefused,
28
36
  onSuccess,
29
37
  onError,
30
38
  onTransactionSent,
31
- );
39
+ });
32
40
 
33
41
  return {
34
42
  cancel: (props: CancelInput) => machine?.start(props),
@@ -15,27 +15,49 @@ interface UseCreateListingArgs
15
15
  extends Omit<UseTransactionMachineConfig, 'type'> {
16
16
  onSuccess?: (hash: Hash) => void;
17
17
  onError?: (error: TransactionError) => void;
18
- onTransactionSent?: (hash: Hash) => void;
18
+ onTransactionSent?: (hash?: Hash, orderId?: string) => void;
19
+ onApprovalSuccess?: (hash: Hash) => void;
20
+ onSwitchChainRefused: () => void;
21
+ enabled: boolean;
19
22
  }
20
23
 
24
+ type ExecutionState = 'approval' | 'listing' | null;
25
+
21
26
  export const useCreateListing = ({
22
27
  onSuccess,
23
28
  onError,
24
29
  onTransactionSent,
30
+ onApprovalSuccess,
31
+ onSwitchChainRefused,
32
+ enabled,
25
33
  ...config
26
34
  }: UseCreateListingArgs) => {
27
35
  const [isLoading, setIsLoading] = useState(false);
28
36
  const [steps, setSteps] = useState<TransactionSteps | null>(null);
37
+ const [executionState, setExecutionState] = useState<ExecutionState>(null);
38
+ const machineConfig = {
39
+ ...config,
40
+ type: TransactionType.LISTING,
41
+ };
29
42
 
30
- const { machine, isLoading: isMachineLoading } = useTransactionMachine(
31
- {
32
- ...config,
33
- type: TransactionType.LISTING,
43
+ const { machine, isLoading: isMachineLoading } = useTransactionMachine({
44
+ config: machineConfig,
45
+ enabled,
46
+ onSuccess: (hash) => {
47
+ setExecutionState(null);
48
+ onSuccess?.(hash);
49
+ },
50
+ onError: (error) => {
51
+ setExecutionState(null);
52
+ onError?.(error);
34
53
  },
35
- onSuccess,
36
- onError,
37
54
  onTransactionSent,
38
- );
55
+ onApprovalSuccess: (hash) => {
56
+ setExecutionState(null);
57
+ onApprovalSuccess?.(hash);
58
+ },
59
+ onSwitchChainRefused,
60
+ });
39
61
 
40
62
  const loadSteps = useCallback(
41
63
  async (props: ListingInput) => {
@@ -46,17 +68,59 @@ export const useCreateListing = ({
46
68
  setIsLoading(false);
47
69
  return;
48
70
  }
49
- setSteps(generatedSteps);
71
+ setSteps({
72
+ ...generatedSteps,
73
+ approval: {
74
+ ...generatedSteps.approval,
75
+ isExecuting: executionState === 'approval',
76
+ },
77
+ transaction: {
78
+ ...generatedSteps.transaction,
79
+ isExecuting: executionState === 'listing',
80
+ },
81
+ });
50
82
  setIsLoading(false);
51
83
  },
52
- [machine, onError],
84
+ [machine, executionState],
85
+ );
86
+
87
+ const handleStepExecution = useCallback(
88
+ async (type: ExecutionState, execute: () => Promise<any> | undefined) => {
89
+ if (!type) return;
90
+ setExecutionState(type);
91
+ try {
92
+ await execute();
93
+ } catch (error) {
94
+ setExecutionState(null);
95
+ throw error;
96
+ }
97
+ },
98
+ [],
53
99
  );
54
100
 
55
101
  return {
56
102
  createListing: (props: ListingInput) => machine?.start(props),
57
103
  getListingSteps: (props: ListingInput) => ({
58
104
  isLoading,
59
- steps,
105
+ steps: steps
106
+ ? {
107
+ ...steps,
108
+ approval: {
109
+ ...steps.approval,
110
+ isExecuting: executionState === 'approval',
111
+ execute: () =>
112
+ handleStepExecution('approval', () => steps.approval.execute()),
113
+ },
114
+ transaction: {
115
+ ...steps.transaction,
116
+ isExecuting: executionState === 'listing',
117
+ execute: () =>
118
+ handleStepExecution('listing', () =>
119
+ steps.transaction.execute(),
120
+ ),
121
+ },
122
+ }
123
+ : null,
60
124
  refreshSteps: () => loadSteps(props),
61
125
  }),
62
126
  isLoading: isMachineLoading,
@@ -1,4 +1,5 @@
1
1
  import { queryOptions, useQuery } from '@tanstack/react-query';
2
+ import { toHex, zeroAddress } from 'viem';
2
3
  import { z } from 'zod';
3
4
  import type { SdkConfig } from '../../types';
4
5
  import { InvalidCurrencyOptionsError } from '../../utils/_internal/error/transaction';
@@ -12,7 +13,6 @@ import {
12
13
  getMarketplaceClient,
13
14
  } from '../_internal';
14
15
  import { useConfig } from './useConfig';
15
- import { toHex, zeroAddress } from 'viem';
16
16
 
17
17
  const ChainIdCoerce = ChainIdSchema.transform((val) => val.toString());
18
18
 
@@ -40,7 +40,6 @@ const fetchCurrencies = async (chainId: ChainId, config: SdkConfig) => {
40
40
  };
41
41
 
42
42
  const selectCurrencies = (data: Currency[], args: UseCurrenciesArgs) => {
43
- console.debug('[selectCurrencies]: Select Currencies Input:', { data, args });
44
43
  const argsParsed = UseCurrenciesArgsSchema.parse(args);
45
44
  // if collectionAddress is passed, filter currencies based on collection currency options
46
45
  if (argsParsed.currencyOptions) {
@@ -1,5 +1,5 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
- import { Address, erc20Abi, formatUnits, zeroAddress } from 'viem';
2
+ import { type Address, erc20Abi, formatUnits, zeroAddress } from 'viem';
3
3
  import { getPublicRpcClient } from '../../utils';
4
4
 
5
5
  export function useCurrencyBalance({
@@ -1,4 +1,4 @@
1
- import { Address } from 'viem';
1
+ import type { Address } from 'viem';
2
2
  import { useMarketplaceConfig } from './useMarketplaceConfig';
3
3
 
4
4
  export const useCurrencyOptions = ({
@@ -14,27 +14,49 @@ import {
14
14
  interface UseMakeOfferArgs extends Omit<UseTransactionMachineConfig, 'type'> {
15
15
  onSuccess?: (hash: Hash) => void;
16
16
  onError?: (error: TransactionError) => void;
17
- onTransactionSent?: (hash: Hash) => void;
17
+ onTransactionSent?: (hash?: Hash, orderId?: string) => void;
18
+ onApprovalSuccess?: (hash: Hash) => void;
19
+ onSwitchChainRefused: () => void;
20
+ enabled: boolean;
18
21
  }
19
22
 
23
+ type ExecutionState = 'approval' | 'offer' | null;
24
+
20
25
  export const useMakeOffer = ({
21
26
  onSuccess,
22
27
  onError,
23
28
  onTransactionSent,
29
+ onApprovalSuccess,
30
+ onSwitchChainRefused,
31
+ enabled,
24
32
  ...config
25
33
  }: UseMakeOfferArgs) => {
26
34
  const [isLoading, setIsLoading] = useState(false);
27
35
  const [steps, setSteps] = useState<TransactionSteps | null>(null);
36
+ const [executionState, setExecutionState] = useState<ExecutionState>(null);
37
+ const machineConfig = {
38
+ ...config,
39
+ type: TransactionType.OFFER,
40
+ };
28
41
 
29
- const { machine, isLoading: isMachineLoading } = useTransactionMachine(
30
- {
31
- ...config,
32
- type: TransactionType.OFFER,
42
+ const { machine, isLoading: isMachineLoading } = useTransactionMachine({
43
+ config: machineConfig,
44
+ enabled,
45
+ onSuccess: (hash) => {
46
+ setExecutionState(null);
47
+ onSuccess?.(hash);
48
+ },
49
+ onError: (error) => {
50
+ setExecutionState(null);
51
+ onError?.(error);
33
52
  },
34
- onSuccess,
35
- onError,
36
53
  onTransactionSent,
37
- );
54
+ onApprovalSuccess: (hash) => {
55
+ setExecutionState(null);
56
+ onApprovalSuccess?.(hash);
57
+ },
58
+ onSwitchChainRefused,
59
+ });
38
60
 
39
61
  const loadSteps = useCallback(
40
62
  async (props: OfferInput) => {
@@ -45,17 +67,57 @@ export const useMakeOffer = ({
45
67
  setIsLoading(false);
46
68
  return;
47
69
  }
48
- setSteps(generatedSteps);
70
+ setSteps({
71
+ ...generatedSteps,
72
+ approval: {
73
+ ...generatedSteps.approval,
74
+ isExecuting: executionState === 'approval',
75
+ },
76
+ transaction: {
77
+ ...generatedSteps.transaction,
78
+ isExecuting: executionState === 'offer',
79
+ },
80
+ });
49
81
  setIsLoading(false);
50
82
  },
51
- [machine, onError],
83
+ [machine, executionState],
84
+ );
85
+
86
+ const handleStepExecution = useCallback(
87
+ async (type: ExecutionState, execute: () => Promise<any> | undefined) => {
88
+ if (!type) return;
89
+ setExecutionState(type);
90
+ try {
91
+ await execute();
92
+ } catch (error) {
93
+ setExecutionState(null);
94
+ throw error;
95
+ }
96
+ },
97
+ [],
52
98
  );
53
99
 
54
100
  return {
55
101
  makeOffer: (props: OfferInput) => machine?.start(props),
56
102
  getMakeOfferSteps: (props: OfferInput) => ({
57
- isLoading: isLoading,
58
- steps,
103
+ isLoading,
104
+ steps: steps
105
+ ? {
106
+ ...steps,
107
+ approval: {
108
+ ...steps.approval,
109
+ isExecuting: executionState === 'approval',
110
+ execute: () =>
111
+ handleStepExecution('approval', () => steps.approval.execute()),
112
+ },
113
+ transaction: {
114
+ ...steps.transaction,
115
+ isExecuting: executionState === 'offer',
116
+ execute: () =>
117
+ handleStepExecution('offer', () => steps.transaction.execute()),
118
+ },
119
+ }
120
+ : null,
59
121
  refreshSteps: () => loadSteps(props),
60
122
  }),
61
123
  isLoading: isMachineLoading,
@@ -10,31 +10,52 @@ import {
10
10
  useTransactionMachine,
11
11
  } from '../_internal/transaction-machine/useTransactionMachine';
12
12
 
13
+ type ExecutionState = 'approval' | 'sell' | null;
13
14
  interface UseSellArgs
14
15
  extends Omit<UseTransactionMachineConfig, 'type' | 'orderbookKind'> {
15
16
  onSuccess?: (hash: Hash) => void;
16
17
  onError?: (error: Error) => void;
17
- onTransactionSent?: (hash: Hash) => void;
18
+ onTransactionSent?: (hash?: Hash) => void;
19
+ onApprovalSuccess?: (hash: Hash) => void;
20
+ onSwitchChainRefused: () => void;
21
+ enabled: boolean;
18
22
  }
19
23
 
20
24
  export const useSell = ({
21
25
  onSuccess,
22
26
  onError,
23
27
  onTransactionSent,
28
+ onApprovalSuccess,
29
+ onSwitchChainRefused,
30
+ enabled,
24
31
  ...config
25
32
  }: UseSellArgs) => {
26
33
  const [isLoading, setIsLoading] = useState(false);
27
34
  const [steps, setSteps] = useState<TransactionSteps | null>(null);
35
+ const [executionState, setExecutionState] = useState<ExecutionState>(null);
36
+ const machineConfig = {
37
+ ...config,
38
+ type: TransactionType.SELL,
39
+ };
28
40
 
29
- const { machine, isLoading: isMachineLoading } = useTransactionMachine(
30
- {
31
- ...config,
32
- type: TransactionType.SELL,
41
+ const { machine, isLoading: isMachineLoading } = useTransactionMachine({
42
+ config: machineConfig,
43
+ enabled,
44
+ onSwitchChainRefused,
45
+ onSuccess: (hash) => {
46
+ setExecutionState(null);
47
+ onSuccess?.(hash);
48
+ },
49
+ onError: (error) => {
50
+ setExecutionState(null);
51
+ onError?.(error);
33
52
  },
34
- onSuccess,
35
- onError,
36
53
  onTransactionSent,
37
- );
54
+ onApprovalSuccess: (hash) => {
55
+ setExecutionState(null);
56
+ onApprovalSuccess?.(hash);
57
+ },
58
+ });
38
59
 
39
60
  const loadSteps = useCallback(
40
61
  async (props: SellInput) => {
@@ -45,17 +66,57 @@ export const useSell = ({
45
66
  setIsLoading(false);
46
67
  return;
47
68
  }
48
- setSteps(generatedSteps);
69
+ setSteps({
70
+ ...generatedSteps,
71
+ approval: {
72
+ ...generatedSteps.approval,
73
+ isExecuting: executionState === 'approval',
74
+ },
75
+ transaction: {
76
+ ...generatedSteps.transaction,
77
+ isExecuting: executionState === 'sell',
78
+ },
79
+ });
49
80
  setIsLoading(false);
50
81
  },
51
- [machine, onError],
82
+ [machine, executionState],
83
+ );
84
+
85
+ const handleStepExecution = useCallback(
86
+ async (type: ExecutionState, execute: () => Promise<any> | undefined) => {
87
+ if (!type) return;
88
+ setExecutionState(type);
89
+ try {
90
+ await execute();
91
+ } catch (error) {
92
+ setExecutionState(null);
93
+ throw error;
94
+ }
95
+ },
96
+ [],
52
97
  );
53
98
 
54
99
  return {
55
100
  sell: (props: SellInput) => machine?.start(props),
56
101
  getSellSteps: (props: SellInput) => ({
57
- isLoading: isLoading,
58
- steps,
102
+ isLoading,
103
+ steps: steps
104
+ ? {
105
+ ...steps,
106
+ approval: {
107
+ ...steps.approval,
108
+ isExecuting: executionState === 'approval',
109
+ execute: () =>
110
+ handleStepExecution('approval', () => steps.approval.execute()),
111
+ },
112
+ transaction: {
113
+ ...steps.transaction,
114
+ isExecuting: executionState === 'sell',
115
+ execute: () =>
116
+ handleStepExecution('sell', () => steps.transaction.execute()),
117
+ },
118
+ }
119
+ : null,
59
120
  refreshSteps: () => loadSteps(props),
60
121
  }),
61
122
  isLoading: isMachineLoading,
@@ -11,13 +11,7 @@ import { useMakeOfferModal } from '../../../modals/MakeOfferModal';
11
11
  import { useSellModal } from '../../../modals/SellModal';
12
12
  import { useTransferModal } from '../../../modals/TransferModal';
13
13
 
14
- export enum CollectibleCardAction {
15
- BUY = 'Buy',
16
- SELL = 'Sell',
17
- LIST = 'Create listing',
18
- OFFER = 'Make an offer',
19
- TRANSFER = 'Transfer',
20
- }
14
+ import { CollectibleCardAction } from './types';
21
15
 
22
16
  type ActionButtonProps = {
23
17
  chainId: string;
@@ -0,0 +1,7 @@
1
+ export enum CollectibleCardAction {
2
+ BUY = 'Buy',
3
+ SELL = 'Sell',
4
+ LIST = 'Create listing',
5
+ OFFER = 'Make an offer',
6
+ TRANSFER = 'Transfer',
7
+ }
@@ -1,22 +1,23 @@
1
1
  import { CheckmarkIcon, ChevronDownIcon } from '@0xsequence/design-system';
2
2
  import * as Select from '@radix-ui/react-select';
3
- import React from 'react';
3
+ import React, { type ReactNode } from 'react';
4
4
  import { content, item, itemIndicator, trigger } from './styles.css';
5
5
 
6
- interface CustomSelectProps {
7
- items: Array<{
8
- value: string;
9
- label: string;
10
- disabled?: boolean;
11
- }>;
6
+ export interface SelectItem {
12
7
  value: string;
8
+ content: ReactNode;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ interface CustomSelectProps {
13
+ items: SelectItem[];
13
14
  onValueChange?: (value: string) => void;
14
- defaultValue?: string;
15
+ defaultValue?: SelectItem;
15
16
  }
16
17
 
17
18
  const CustomSelectItem = React.forwardRef<
18
19
  HTMLDivElement,
19
- Select.SelectItemProps
20
+ Select.SelectItemProps & { children: ReactNode }
20
21
  >(({ children, ...props }, forwardedRef) => {
21
22
  return (
22
23
  <Select.Item className={item} {...props} ref={forwardedRef}>
@@ -30,13 +31,15 @@ const CustomSelectItem = React.forwardRef<
30
31
 
31
32
  export const CustomSelect: React.FC<CustomSelectProps> = ({
32
33
  items,
33
- value,
34
34
  onValueChange,
35
35
  defaultValue,
36
36
  }) => {
37
37
  return (
38
- <Select.Root onValueChange={onValueChange} defaultValue={defaultValue}>
39
- <Select.Trigger className={trigger} value={value}>
38
+ <Select.Root
39
+ onValueChange={onValueChange}
40
+ defaultValue={defaultValue?.value}
41
+ >
42
+ <Select.Trigger className={trigger}>
40
43
  <Select.Value />
41
44
  <Select.Icon>
42
45
  <ChevronDownIcon size="xs" />
@@ -46,13 +49,13 @@ export const CustomSelect: React.FC<CustomSelectProps> = ({
46
49
  <Select.Portal>
47
50
  <Select.Content className={content}>
48
51
  <Select.Viewport>
49
- {items.map((item, index) => (
52
+ {items.map((item) => (
50
53
  <CustomSelectItem
51
- key={index}
54
+ key={item.value}
52
55
  value={item.value}
53
56
  disabled={item.disabled}
54
57
  >
55
- {item.label}
58
+ {item.content}
56
59
  </CustomSelectItem>
57
60
  ))}
58
61
  </Select.Viewport>