@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
@@ -4,32 +4,33 @@ import {
4
4
  Skeleton,
5
5
  Text,
6
6
  } from '@0xsequence/design-system';
7
+ import { TRANSACTION_CONFIRMATIONS_DEFAULT } from '@0xsequence/kit';
8
+ import type { ChainId } from '@0xsequence/network';
7
9
  import { observer } from '@legendapp/state/react';
8
10
  import { Close, Content, Overlay, Portal, Root } from '@radix-ui/react-dialog';
9
- import { type QueryKey } from '@tanstack/react-query';
11
+ import type { QueryKey } from '@tanstack/react-query';
10
12
  import { useEffect, useState } from 'react';
11
- import { WaitForTransactionReceiptTimeoutError, type Hex } from 'viem';
13
+ import { type Hex, WaitForTransactionReceiptTimeoutError } from 'viem';
12
14
  import type { Price } from '../../../../../../types';
15
+ import { getPublicRpcClient } from '../../../../../../utils';
13
16
  import { getQueryClient } from '../../../../../_internal';
17
+ import type { TransactionType } from '../../../../../_internal/transaction-machine/execute-transaction';
14
18
  import { useCollectible } from '../../../../../hooks';
19
+ import type { ModalCallbacks } from '../../types';
15
20
  import TransactionFooter from '../transaction-footer';
16
21
  import TransactionPreview from '../transactionPreview';
17
- import { TransactionStatus, transactionStatusModal$ } from './store';
22
+ import { type TransactionStatus, transactionStatusModal$ } from './store';
18
23
  import {
19
24
  closeButton,
20
25
  dialogOverlay,
21
26
  transactionStatusModalContent,
22
27
  } from './styles.css';
23
- import { ChainId } from '@0xsequence/network';
24
- import { getPublicRpcClient } from '../../../../../../utils';
25
- import { TRANSACTION_CONFIRMATIONS_DEFAULT } from '@0xsequence/kit';
26
- import { TransactionType } from '../../../../../_internal/transaction-machine/execute-transaction';
27
- import { ModalCallbacks } from '../../types';
28
- import { getTransactionStatusModalTitle } from './util/getTitle';
29
28
  import { getTransactionStatusModalMessage } from './util/getMessage';
29
+ import { getTransactionStatusModalTitle } from './util/getTitle';
30
30
 
31
31
  export type ShowTransactionStatusModalArgs = {
32
- hash: Hex;
32
+ hash?: Hex;
33
+ orderId?: string;
33
34
  price?: Price;
34
35
  collectionAddress: Hex;
35
36
  chainId: string;
@@ -56,6 +57,7 @@ const TransactionStatusModal = observer(() => {
56
57
  const {
57
58
  type,
58
59
  hash,
60
+ orderId,
59
61
  price,
60
62
  collectionAddress,
61
63
  chainId,
@@ -69,26 +71,11 @@ const TransactionStatusModal = observer(() => {
69
71
  chainId,
70
72
  collectibleId,
71
73
  });
72
- const [transactionStatus, setTransactionStatus] =
73
- useState<TransactionStatus>('PENDING');
74
- const title = getTransactionStatusModalTitle({
75
- transactionStatus,
76
- transactionType: type!,
77
- });
78
- const message = getTransactionStatusModalMessage({
79
- transactionStatus,
80
- transactionType: type!,
81
- collectibleName: collectible?.name || '',
82
- });
83
- const { onError, onSuccess }: ModalCallbacks = callbacks || {};
84
- const queryClient = getQueryClient();
85
- const publicClient = chainId ? getPublicRpcClient(chainId) : null;
86
- const waitForTransactionReceiptPromise =
87
- publicClient?.waitForTransactionReceipt({
88
- confirmations: confirmations || TRANSACTION_CONFIRMATIONS_DEFAULT,
89
- hash: hash!,
90
- });
74
+ const [transactionStatus, setTransactionStatus] = useState<TransactionStatus>(
75
+ orderId ? 'SUCCESS' : 'PENDING',
76
+ );
91
77
 
78
+ // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
92
79
  useEffect(() => {
93
80
  if (!transactionStatusModal$.isOpen.get()) return;
94
81
 
@@ -98,9 +85,20 @@ const TransactionStatusModal = observer(() => {
98
85
  if (receipt.status === 'success') {
99
86
  console.log('receipt', receipt);
100
87
  setTransactionStatus('SUCCESS');
88
+ if (callbacks?.onSuccess) {
89
+ callbacks.onSuccess(hash || '0x');
90
+ } else {
91
+ console.debug('onSuccess callback not provided:', hash);
92
+ }
101
93
  }
102
94
  })
103
95
  .catch((error) => {
96
+ if (callbacks?.onError) {
97
+ callbacks.onError(error);
98
+ } else {
99
+ console.debug('onError callback not provided:', error);
100
+ }
101
+
104
102
  if (error instanceof WaitForTransactionReceiptTimeoutError) {
105
103
  setTransactionStatus('TIMEOUT');
106
104
  return;
@@ -116,7 +114,36 @@ const TransactionStatusModal = observer(() => {
116
114
  return () => {
117
115
  setTransactionStatus('PENDING');
118
116
  };
119
- }, [onSuccess, onError, transactionStatusModal$.isOpen.get()]);
117
+ }, [
118
+ callbacks?.onSuccess,
119
+ callbacks?.onError,
120
+ transactionStatusModal$.isOpen.get(),
121
+ ]);
122
+
123
+ if (!type) {
124
+ return null;
125
+ }
126
+
127
+ const title = getTransactionStatusModalTitle({
128
+ transactionStatus,
129
+ transactionType: type,
130
+ orderId,
131
+ });
132
+
133
+ const message = getTransactionStatusModalMessage({
134
+ transactionStatus,
135
+ transactionType: type,
136
+ collectibleName: collectible?.name || '',
137
+ orderId,
138
+ });
139
+
140
+ const queryClient = getQueryClient();
141
+ const publicClient = chainId ? getPublicRpcClient(chainId) : null;
142
+ const waitForTransactionReceiptPromise =
143
+ publicClient?.waitForTransactionReceipt({
144
+ confirmations: confirmations || TRANSACTION_CONFIRMATIONS_DEFAULT,
145
+ hash: hash || '0x',
146
+ });
120
147
 
121
148
  return (
122
149
  <Root open={transactionStatusModal$.isOpen.get()}>
@@ -146,6 +173,7 @@ const TransactionStatusModal = observer(() => {
146
173
  )}
147
174
 
148
175
  <TransactionPreview
176
+ orderId={orderId}
149
177
  price={price}
150
178
  collectionAddress={collectionAddress}
151
179
  chainId={chainId}
@@ -159,7 +187,8 @@ const TransactionStatusModal = observer(() => {
159
187
  />
160
188
 
161
189
  <TransactionFooter
162
- transactionHash={hash!}
190
+ transactionHash={hash}
191
+ orderId={orderId}
163
192
  isConfirming={transactionStatus === 'PENDING'}
164
193
  isConfirmed={transactionStatus === 'SUCCESS'}
165
194
  isFailed={transactionStatus === 'FAILED'}
@@ -3,8 +3,8 @@ import type { QueryKey } from '@tanstack/react-query';
3
3
  import type { Hex } from 'viem';
4
4
  import type { ShowTransactionStatusModalArgs } from '.';
5
5
  import type { Price } from '../../../../../../types';
6
- import { TransactionType } from '../../../../../_internal/transaction-machine/execute-transaction';
7
- import { ModalCallbacks } from '../../types';
6
+ import type { TransactionType } from '../../../../../_internal/transaction-machine/execute-transaction';
7
+ import type { ModalCallbacks } from '../../types';
8
8
 
9
9
  export type ConfirmationStatus = {
10
10
  isConfirming: boolean;
@@ -21,6 +21,7 @@ export interface TransactionStatusModalState {
21
21
  close: () => void;
22
22
  state: {
23
23
  hash: Hex | undefined;
24
+ orderId: string | undefined;
24
25
  status: TransactionStatus;
25
26
  type: TransactionType | undefined;
26
27
  price: Price | undefined;
@@ -38,6 +39,7 @@ export const initialState: TransactionStatusModalState = {
38
39
  isOpen: false,
39
40
  open: ({
40
41
  hash,
42
+ orderId,
41
43
  price,
42
44
  collectionAddress,
43
45
  chainId,
@@ -51,6 +53,7 @@ export const initialState: TransactionStatusModalState = {
51
53
  transactionStatusModal$.state.set({
52
54
  ...transactionStatusModal$.state.get(),
53
55
  hash,
56
+ orderId,
54
57
  price,
55
58
  collectionAddress,
56
59
  chainId,
@@ -71,6 +74,7 @@ export const initialState: TransactionStatusModalState = {
71
74
  },
72
75
  state: {
73
76
  hash: undefined,
77
+ orderId: undefined,
74
78
  status: 'PENDING',
75
79
  price: undefined,
76
80
  collectionAddress: '' as Hex,
@@ -2,7 +2,7 @@ import { TransactionType } from '../../../../../../_internal/transaction-machine
2
2
 
3
3
  export function getFormattedType(
4
4
  transactionType: TransactionType,
5
- verb: boolean = false,
5
+ verb = false,
6
6
  ): string {
7
7
  switch (transactionType) {
8
8
  case TransactionType.TRANSFER:
@@ -1,19 +1,25 @@
1
- import { TransactionType } from '../../../../../../_internal/transaction-machine/execute-transaction';
2
- import { TransactionStatus } from '../store';
1
+ import type { TransactionType } from '../../../../../../_internal/transaction-machine/execute-transaction';
2
+ import type { TransactionStatus } from '../store';
3
3
  import { getFormattedType } from './getFormattedType';
4
4
 
5
5
  export function getTransactionStatusModalMessage({
6
6
  transactionStatus,
7
7
  transactionType,
8
8
  collectibleName,
9
+ orderId,
9
10
  }: {
10
11
  transactionStatus: TransactionStatus;
11
12
  transactionType: TransactionType;
12
13
  collectibleName: string;
14
+ orderId?: string;
13
15
  }): string {
14
16
  // without this, the text will be "Your cancellation CollectibleXXX has failed." which sounds weird
15
17
  const hideCollectibleName = transactionType === 'CANCEL';
16
18
 
19
+ if (orderId) {
20
+ return `You just ${getFormattedType(transactionType, true)}${!hideCollectibleName ? ` ${collectibleName}` : ''}. It's been confirmed on the blockchain!`;
21
+ }
22
+
17
23
  switch (transactionStatus) {
18
24
  case 'PENDING':
19
25
  return `You just ${getFormattedType(transactionType, true)}${!hideCollectibleName ? ` ${collectibleName}` : ''}. It should be confirmed on the blockchain shortly.`;
@@ -1,14 +1,23 @@
1
- import { TransactionType } from '../../../../../../_internal/transaction-machine/execute-transaction';
2
- import { TransactionStatus } from '../store';
1
+ import type { TransactionType } from '../../../../../../_internal/transaction-machine/execute-transaction';
2
+ import type { TransactionStatus } from '../store';
3
3
  import { getFormattedType } from './getFormattedType';
4
4
 
5
5
  export function getTransactionStatusModalTitle({
6
6
  transactionStatus,
7
7
  transactionType,
8
+ orderId,
8
9
  }: {
9
10
  transactionStatus: TransactionStatus;
10
- transactionType: TransactionType;
11
+ transactionType: TransactionType | undefined;
12
+ orderId?: string;
11
13
  }): string {
14
+ if (transactionType === undefined) {
15
+ return '';
16
+ }
17
+ if (orderId) {
18
+ return `Your ${getFormattedType(transactionType)} has processed`;
19
+ }
20
+
12
21
  switch (transactionStatus) {
13
22
  case 'PENDING':
14
23
  return `Your ${getFormattedType(transactionType)} is processing`;
@@ -0,0 +1,146 @@
1
+ import {
2
+ Box,
3
+ Button,
4
+ Skeleton,
5
+ Text,
6
+ WarningIcon,
7
+ } from '@0xsequence/design-system';
8
+ import { useWaasFeeOptions } from '@0xsequence/kit';
9
+ import { observer } from '@legendapp/state/react';
10
+ import { useEffect } from 'react';
11
+ import { type Hex, zeroAddress } from 'viem';
12
+ import { useAccount } from 'wagmi';
13
+ import { useCurrencyBalance } from '../../../../../hooks/useCurrencyBalance';
14
+ import WaasFeeOptionsSelect, {
15
+ type FeeOption,
16
+ } from '../waasFeeOptionsSelect/WaasFeeOptionsSelect';
17
+ import { waasFeeOptionsModal$ } from './store';
18
+ import { feeOptionsWrapper } from './styles.css';
19
+
20
+ type WaasFeeOptionsBoxProps = {
21
+ onFeeOptionsLoaded: () => void;
22
+ onFeeOptionConfirmed: () => void;
23
+ chainId: number;
24
+ };
25
+
26
+ const WaasFeeOptionsBox = observer(
27
+ ({
28
+ onFeeOptionsLoaded,
29
+ onFeeOptionConfirmed,
30
+ chainId,
31
+ }: WaasFeeOptionsBoxProps) => {
32
+ const { address: userAddress } = useAccount();
33
+ const selectedFeeOption$ = waasFeeOptionsModal$.selectedFeeOption;
34
+ const [pendingFeeOptionConfirmation, confirmPendingFeeOption] =
35
+ useWaasFeeOptions();
36
+ const { data: currencyBalance, isLoading: currencyBalanceLoading } =
37
+ useCurrencyBalance({
38
+ chainId,
39
+ currencyAddress: (selectedFeeOption$.token.contractAddress.get() ||
40
+ zeroAddress) as Hex,
41
+ userAddress: userAddress as Hex,
42
+ });
43
+
44
+ // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
45
+ useEffect(() => {
46
+ if (pendingFeeOptionConfirmation) {
47
+ onFeeOptionsLoaded();
48
+ }
49
+ }, [pendingFeeOptionConfirmation]);
50
+
51
+ const selectedFeeOption = selectedFeeOption$.get();
52
+ const insufficientBalance = (() => {
53
+ if (!selectedFeeOption?.value || !selectedFeeOption.token.decimals) {
54
+ return false;
55
+ }
56
+
57
+ if (!currencyBalance?.value && currencyBalance?.value !== 0n) {
58
+ return true;
59
+ }
60
+
61
+ try {
62
+ const feeValue = BigInt(selectedFeeOption.value);
63
+ return currencyBalance.value === 0n || currencyBalance.value < feeValue;
64
+ } catch {
65
+ return true;
66
+ }
67
+ })();
68
+
69
+ const handleConfirmFeeOption = () => {
70
+ if (!selectedFeeOption?.token || !pendingFeeOptionConfirmation?.id)
71
+ return;
72
+
73
+ confirmPendingFeeOption(
74
+ pendingFeeOptionConfirmation?.id,
75
+ selectedFeeOption.token.contractAddress || zeroAddress,
76
+ );
77
+
78
+ onFeeOptionConfirmed();
79
+ };
80
+
81
+ if (!pendingFeeOptionConfirmation) return null;
82
+
83
+ return (
84
+ <Box className={feeOptionsWrapper}>
85
+ <Text
86
+ fontSize="medium"
87
+ fontFamily="body"
88
+ fontWeight="bold"
89
+ marginBottom="2"
90
+ >
91
+ Select a fee option
92
+ </Text>
93
+
94
+ <WaasFeeOptionsSelect
95
+ options={(pendingFeeOptionConfirmation?.options as FeeOption[]) || []}
96
+ selectedFeeOption$={selectedFeeOption$}
97
+ />
98
+
99
+ <Box
100
+ display="flex"
101
+ alignItems="center"
102
+ justifyContent="space-between"
103
+ width="full"
104
+ >
105
+ {currencyBalanceLoading ? (
106
+ <Skeleton style={{ height: 15 }} borderRadius="md" width="1/3" />
107
+ ) : (
108
+ <Box display="flex" alignItems="center" gap="2">
109
+ {insufficientBalance && (
110
+ <WarningIcon color="negative" size="xs" />
111
+ )}
112
+ <Text
113
+ fontSize="small"
114
+ fontWeight="semibold"
115
+ fontFamily="body"
116
+ color={insufficientBalance ? 'negative' : 'text100'}
117
+ >
118
+ You have {currencyBalance?.formatted || '0'}{' '}
119
+ {selectedFeeOption?.token.symbol}
120
+ </Text>
121
+ </Box>
122
+ )}
123
+
124
+ <Button
125
+ disabled={
126
+ !selectedFeeOption?.token ||
127
+ insufficientBalance ||
128
+ currencyBalanceLoading
129
+ }
130
+ pending={currencyBalanceLoading}
131
+ onClick={handleConfirmFeeOption}
132
+ label={
133
+ <Box display="flex" alignItems="center" gap="2">
134
+ Confirm
135
+ </Box>
136
+ }
137
+ variant={insufficientBalance ? 'danger' : 'primary'}
138
+ size="xs"
139
+ />
140
+ </Box>
141
+ </Box>
142
+ );
143
+ },
144
+ );
145
+
146
+ export default WaasFeeOptionsBox;
@@ -0,0 +1,12 @@
1
+ import { observable } from '@legendapp/state';
2
+ import type { FeeOption } from '../waasFeeOptionsSelect/WaasFeeOptionsSelect';
3
+
4
+ type WaasFeeOptionsModalState = {
5
+ selectedFeeOption: FeeOption | undefined;
6
+ };
7
+
8
+ const initialState = {
9
+ selectedFeeOption: undefined,
10
+ } as WaasFeeOptionsModalState;
11
+
12
+ export const waasFeeOptionsModal$ = observable(initialState);
@@ -0,0 +1,53 @@
1
+ import { atoms } from '@0xsequence/design-system';
2
+ import { globalStyle, style } from '@vanilla-extract/css';
3
+
4
+ export const feeOptionsWrapper = style([
5
+ atoms({
6
+ position: 'absolute',
7
+ background: 'buttonEmphasis',
8
+ backdropFilter: 'blur',
9
+ width: 'full',
10
+ left: '0',
11
+ borderRadius: 'lg',
12
+ display: 'flex',
13
+ flexDirection: 'column',
14
+ gap: '2',
15
+ padding: '4',
16
+ }),
17
+ {
18
+ bottom: '-140px',
19
+ },
20
+ ]);
21
+
22
+ export const dialogOverlay = style([
23
+ atoms({
24
+ background: 'backgroundBackdrop',
25
+ position: 'fixed',
26
+ inset: '0',
27
+ zIndex: '50',
28
+ }),
29
+ ]);
30
+
31
+ export const dialogContent = style([
32
+ atoms({
33
+ display: 'flex',
34
+ background: 'backgroundPrimary',
35
+ borderRadius: 'lg',
36
+ position: 'fixed',
37
+ zIndex: '50',
38
+ }),
39
+ {
40
+ top: '50%',
41
+ left: '50%',
42
+ transform: 'translate(-50%, -50%)',
43
+ padding: '24px',
44
+ },
45
+ ]);
46
+
47
+ export const cta = style({
48
+ borderRadius: '12px !important',
49
+ });
50
+
51
+ globalStyle(`${cta} > div`, {
52
+ justifyContent: 'center !important',
53
+ });
@@ -0,0 +1,117 @@
1
+ import { Box, Image, Text } from '@0xsequence/design-system';
2
+ import type { Observable } from '@legendapp/state';
3
+ import { observer } from '@legendapp/state/react';
4
+ import { useEffect } from 'react';
5
+ import { formatUnits } from 'viem';
6
+ import {
7
+ CustomSelect,
8
+ type SelectItem,
9
+ } from '../../../../components/_internals/custom-select/CustomSelect';
10
+
11
+ export type FeeOption = {
12
+ gasLimit: number;
13
+ to: string;
14
+ token: {
15
+ chainId: number;
16
+ contractAddress: string | null;
17
+ decimals: number;
18
+ logoURL: string;
19
+ name: string;
20
+ symbol: string;
21
+ tokenID: string | null;
22
+ type: string;
23
+ };
24
+ value: string;
25
+ };
26
+
27
+ const WaasFeeOptionsSelect = observer(
28
+ ({
29
+ options,
30
+ selectedFeeOption$,
31
+ }: {
32
+ options: FeeOption[];
33
+ selectedFeeOption$: Observable<FeeOption | undefined>;
34
+ }) => {
35
+ const feeOptions = options
36
+ .filter((option) => option.token.contractAddress !== null)
37
+ .map((option) => {
38
+ const value = option.token.contractAddress ?? '';
39
+ return FeeOptionSelectItem({ value, option });
40
+ });
41
+
42
+ // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
43
+ useEffect(() => {
44
+ if (options.length > 0 && !selectedFeeOption$.get())
45
+ selectedFeeOption$.set(options[0]);
46
+ }, [options]);
47
+
48
+ if (options.length === 0 || !selectedFeeOption$.get()?.token) return null;
49
+
50
+ return (
51
+ <CustomSelect
52
+ items={feeOptions}
53
+ onValueChange={(value) => {
54
+ const selectedOption = options.find(
55
+ (option) => option.token.contractAddress === value,
56
+ );
57
+
58
+ selectedFeeOption$.set(selectedOption);
59
+ }}
60
+ defaultValue={
61
+ selectedFeeOption$.get()?.token.contractAddress
62
+ ? FeeOptionSelectItem({
63
+ value: selectedFeeOption$.get()?.token.contractAddress ?? '',
64
+ option: selectedFeeOption$.get() ?? options[0],
65
+ })
66
+ : undefined
67
+ }
68
+ />
69
+ );
70
+ },
71
+ );
72
+
73
+ function FeeOptionSelectItem({
74
+ value,
75
+ option,
76
+ }: {
77
+ value: string;
78
+ option: FeeOption;
79
+ }) {
80
+ return {
81
+ value,
82
+ content: (
83
+ <Box display="flex" alignItems="center" gap="2">
84
+ <Image
85
+ src={option.token.logoURL}
86
+ alt={option.token.symbol}
87
+ width="3"
88
+ height="3"
89
+ />
90
+
91
+ <Box display="flex" gap="1">
92
+ <Text color="text100" fontSize="small" fontFamily="body">
93
+ Fee
94
+ </Text>
95
+ <Text
96
+ color="text50"
97
+ fontSize="small"
98
+ fontFamily="body"
99
+ fontWeight="semibold"
100
+ >
101
+ (in {option.token.symbol})
102
+ </Text>
103
+
104
+ <Text color="text100" fontSize="small" fontFamily="body">
105
+ :
106
+ </Text>
107
+ </Box>
108
+
109
+ <Text fontSize="small" fontFamily="body">
110
+ {formatUnits(BigInt(option.value), option.token.decimals || 0)}
111
+ </Text>
112
+ </Box>
113
+ ),
114
+ } as SelectItem;
115
+ }
116
+
117
+ export default WaasFeeOptionsSelect;
@@ -1,4 +1,4 @@
1
- import { Address } from 'viem';
1
+ import type { Address } from 'viem';
2
2
  import { BaseError } from './base';
3
3
 
4
4
  export type TransactionErrorType<name extends string = 'TransactionError'> =
@@ -84,6 +84,16 @@ export class InsufficientFundsError extends TransactionError {
84
84
  }
85
85
  }
86
86
 
87
+ export class SignatureExecutionError extends TransactionError {
88
+ override name = 'SignatureExecutionError';
89
+ constructor(signature: string, cause?: Error) {
90
+ super(`Failed to execute signature: ${signature}`, {
91
+ details: cause?.message || 'The execution of the signature failed.',
92
+ cause,
93
+ });
94
+ }
95
+ }
96
+
87
97
  export class StepExecutionError extends TransactionError {
88
98
  override name = 'StepExecutionError';
89
99
  constructor(stepId: string, cause?: Error) {
@@ -253,7 +263,7 @@ export class InvalidCurrencyOptionsError extends TransactionError {
253
263
  export class ProjectNotFoundError extends TransactionError {
254
264
  override name = 'ProjectNotFoundError';
255
265
  constructor(projectId: string, url: string) {
256
- super(`Project not found`, {
266
+ super('Project not found', {
257
267
  details: `Project id: ${projectId} not found at ${url}`,
258
268
  });
259
269
  }
@@ -11,11 +11,10 @@ export const calculatePriceDifferencePercentage = ({
11
11
  basePriceRaw,
12
12
  decimals,
13
13
  }: CalculatePriceDifferencePercentageArgs) => {
14
- const difference = Number(
15
- formatUnits(inputPriceRaw - basePriceRaw, decimals),
16
- );
14
+ const inputPrice = Number(formatUnits(inputPriceRaw, decimals));
17
15
  const basePrice = Number(formatUnits(basePriceRaw, decimals));
16
+ const difference = inputPrice - basePrice;
18
17
  const percentageDifference = (difference / basePrice) * 100;
19
18
 
20
- return Math.abs(percentageDifference).toFixed(2);
19
+ return percentageDifference.toFixed(2);
21
20
  };
package/tsconfig.json CHANGED
@@ -1,24 +1,4 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "useDefineForClassFields": true,
5
- "lib": ["dom", "dom.iterable", "esnext"],
6
-
7
- "module": "ESNext",
8
- "skipLibCheck": true,
9
-
10
- "moduleResolution": "bundler",
11
- "resolvePackageJsonImports": true,
12
- "allowImportingTsExtensions": true,
13
- "isolatedModules": true,
14
- "moduleDetection": "force",
15
- "noEmit": true,
16
- "jsx": "react-jsx",
17
-
18
- "strict": true,
19
- "noUnusedLocals": true,
20
- "noUnusedParameters": true,
21
- "noFallthroughCasesInSwitch": true
22
- },
2
+ "extends": "../../tsconfig.json",
23
3
  "include": ["src", "tsup.config.ts"]
24
4
  }