@0xsequence/marketplace-sdk 2.0.0 → 2.0.2
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.
- package/CHANGELOG.md +18 -0
- package/dist/BellIcon.js +1 -1
- package/dist/Card.js +1 -1
- package/dist/ShopCard.d.ts +4 -4
- package/dist/builder-api.js +1 -1
- package/dist/collectible.js +2 -2
- package/dist/collectible.js.map +1 -1
- package/dist/collection.js +1 -1
- package/dist/create-config.d.ts +589 -193
- package/dist/create-config.js +1 -1
- package/dist/currency.js +3 -3
- package/dist/currency.js.map +1 -1
- package/dist/dist.js +167 -148
- package/dist/dist.js.map +1 -1
- package/dist/expirationDateSelect.js +1 -1
- package/dist/filter-state.d.ts +1 -1
- package/dist/filters.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index10.d.ts +1 -1
- package/dist/index11.d.ts +17 -17
- package/dist/index12.d.ts +21 -21
- package/dist/index14.d.ts +3 -3
- package/dist/index15.d.ts +3 -3
- package/dist/index16.d.ts +2 -2
- package/dist/index17.d.ts +75 -75
- package/dist/index18.d.ts +40 -40
- package/dist/index19.d.ts +5 -5
- package/dist/index2.d.ts +4 -1
- package/dist/index21.d.ts +15 -15
- package/dist/index22.d.ts +8 -65
- package/dist/index23.d.ts +21 -13
- package/dist/index26.d.ts +4 -4
- package/dist/index27.d.ts +4 -4
- package/dist/index28.d.ts +10 -10
- package/dist/index3.d.ts +2 -2194
- package/dist/index31.d.ts +5 -5
- package/dist/index33.d.ts +3 -3
- package/dist/index34.d.ts +1 -1
- package/dist/index35.d.ts +1 -1
- package/dist/index36.d.ts +5 -5
- package/dist/index37.d.ts +8 -6
- package/dist/index38.d.ts +5 -5
- package/dist/index39.d.ts +1 -1
- package/dist/index4.d.ts +1356 -1356
- package/dist/index40.d.ts +2 -2
- package/dist/index8.d.ts +11 -3
- package/dist/index9.d.ts +2811 -3
- package/dist/inventory.d.ts +4 -4
- package/dist/inventory.js +3 -3
- package/dist/inventory.js.map +1 -1
- package/dist/marketplace2.js +3 -3
- package/dist/marketplace2.js.map +1 -1
- package/dist/metadata.d.ts +41 -41
- package/dist/primary-sale-checkout-options.d.ts +4 -4
- package/dist/quantityInput.js +1 -1
- package/dist/ranges.d.ts +12 -12
- package/dist/react/_internal/index.d.ts +1 -1
- package/dist/react/_internal/index.js +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/queries/collectible/index.d.ts +1 -1
- package/dist/react/queries/index.d.ts +1 -1
- package/dist/react/ssr/index.d.ts +3 -3
- package/dist/react/ssr/index.js +3 -3
- package/dist/react/ui/components/marketplace-collectible-card/index.d.ts +1 -1
- package/dist/react/ui/modals/CreateListingModal/internal/hooks/index.d.ts +1 -1
- package/dist/react/ui/modals/MakeOfferModal/internal/hooks/index.d.ts +1 -1
- package/dist/react/ui/modals/_internal/components/alertMessage/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/baseModal/index.d.ts +6 -6
- package/dist/react/ui/modals/_internal/components/calendar/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/currencyImage/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/currencyOptionsSelect/index.d.ts +3 -3
- package/dist/react/ui/modals/_internal/components/floorPriceText/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/priceInput/index.d.ts +3 -5
- package/dist/react/ui/modals/_internal/components/quantityInput/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/selectWaasFeeOptions/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/switchChainErrorModal/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/timeAgo/index.d.ts +2 -2
- package/dist/react/ui/modals/_internal/components/tokenPreview/index.d.ts +3 -3
- package/dist/react/ui/modals/_internal/components/transaction-footer/index.d.ts +3 -3
- package/dist/react/ui/modals/_internal/components/transactionDetails/index.d.ts +3 -3
- package/dist/react/ui/modals/_internal/components/transactionPreview/index.d.ts +3 -3
- package/dist/react/ui/modals/_internal/components/transactionStatusModal/index.d.ts +3 -3
- package/dist/react.js +2279 -1919
- package/dist/react.js.map +1 -1
- package/dist/styles/index.css +15 -0
- package/dist/token-balances.d.ts +28 -28
- package/dist/transaction-footer.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/url-state.js +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/utils.js +31 -4
- package/dist/utils.js.map +1 -1
- package/package.json +7 -5
- package/src/react/hooks/config/useMarketplaceConfig.test.tsx +1 -0
- package/src/react/hooks/currency/list.test.tsx +23 -2
- package/src/react/hooks/transactions/useCancelTransactionSteps.tsx +4 -1
- package/src/react/hooks/transactions/useMarketTransactionSteps.tsx +55 -15
- package/src/react/hooks/utils/useEnsureCorrectChain.ts +10 -5
- package/src/react/queries/collectible/market-list.ts +5 -3
- package/src/react/queries/currency/list.ts +8 -5
- package/src/react/queries/inventory/inventory.ts +5 -3
- package/src/react/queries/marketplace/filters.ts +5 -3
- package/src/react/ui/modals/BuyModal/components/BuyModalContent.tsx +74 -37
- package/src/react/ui/modals/BuyModal/components/CryptoPaymentModal.tsx +74 -11
- package/src/react/ui/modals/BuyModal/components/Modal.tsx +62 -1
- package/src/react/ui/modals/BuyModal/hooks/useExecuteBundledTransactions.ts +13 -26
- package/src/react/ui/modals/BuyModal/hooks/useMarketPlatformFee.ts +5 -5
- package/src/react/ui/modals/BuyModal/internal/__tests__/buildTrailsMarketBuyActions.test.ts +213 -0
- package/src/react/ui/modals/BuyModal/internal/buildTrailsMarketBuyActions.ts +259 -0
- package/src/react/ui/modals/BuyModal/internal/buyModalContext.ts +79 -10
- package/src/react/ui/modals/BuyModal/internal/cryptoPaymentModalContext.tsx +44 -17
- package/src/react/ui/modals/CreateListingModal/internal/store.ts +5 -2
- package/src/react/ui/modals/MakeOfferModal/internal/context.ts +21 -1
- package/src/react/ui/modals/MakeOfferModal/internal/helpers/validation.ts +16 -1
- package/src/react/ui/modals/MakeOfferModal/internal/store.ts +5 -2
- package/src/react/ui/modals/SellModal/internal/store.ts +5 -2
- package/src/react/ui/modals/_internal/components/baseModal/errors/ModalInitializationError.tsx +8 -6
- package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +2 -1
- package/src/react/ui/modals/_internal/components/priceInput/index.tsx +13 -19
- package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +5 -2
- package/src/react/ui/modals/_internal/helpers/currency.test.ts +27 -0
- package/src/react/ui/modals/_internal/helpers/currency.ts +4 -2
- package/src/styles/styles.ts +18 -0
- package/src/utils/__tests__/getMarketplaceDetails.test.ts +10 -0
- package/src/utils/__tests__/getWebRPCErrorMessage.test.ts +28 -0
- package/src/utils/__tests__/marketplaceNormalization.test.ts +38 -0
- package/src/utils/collection.ts +19 -0
- package/src/utils/getConduitAddressForOrderbook.ts +2 -10
- package/src/utils/getMarketplaceDetails.ts +11 -4
- package/src/utils/getWebRPCErrorMessage.ts +21 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/normalizeMarketplace.ts +31 -0
|
@@ -26,13 +26,18 @@ export const useEnsureCorrectChain = () => {
|
|
|
26
26
|
const ensureCorrectChainAsync = useCallback(
|
|
27
27
|
async (targetChainId: number) => {
|
|
28
28
|
if (currentChainId === targetChainId) {
|
|
29
|
-
return
|
|
29
|
+
return true;
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
|
|
32
|
+
return switchChainAsync({ chainId: targetChainId })
|
|
33
|
+
.then(() => true)
|
|
34
|
+
.catch(() => {
|
|
35
|
+
showSwitchChainErrorModal({
|
|
36
|
+
chainIdToSwitchTo: targetChainId,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return false;
|
|
34
40
|
});
|
|
35
|
-
});
|
|
36
41
|
},
|
|
37
42
|
[currentChainId, isWaaS, switchChainAsync, showSwitchChainErrorModal],
|
|
38
43
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isAddress } from 'viem';
|
|
2
2
|
import type { Page } from '../../../types';
|
|
3
3
|
import type { CardType } from '../../../types/types';
|
|
4
|
-
import {
|
|
4
|
+
import { findMarketCollection } from '../../../utils';
|
|
5
5
|
import type {
|
|
6
6
|
ListCollectiblesRequest,
|
|
7
7
|
ListCollectiblesResponse,
|
|
@@ -37,8 +37,10 @@ export async function fetchListCollectibles(
|
|
|
37
37
|
params;
|
|
38
38
|
const marketplaceClient = getMarketplaceClient(config);
|
|
39
39
|
const marketplaceConfig = await fetchMarketplaceConfig({ config });
|
|
40
|
-
const isMarketCollection =
|
|
41
|
-
|
|
40
|
+
const isMarketCollection = !!findMarketCollection(
|
|
41
|
+
marketplaceConfig?.market.collections ?? [],
|
|
42
|
+
collectionAddress,
|
|
43
|
+
chainId,
|
|
42
44
|
);
|
|
43
45
|
|
|
44
46
|
// If it's not a market collection, return an empty list. those collections are not compatible with the ListCollectibles endpoint.
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
ListCurrenciesRequest,
|
|
5
5
|
} from '@0xsequence/api-client';
|
|
6
6
|
import { zeroAddress } from 'viem';
|
|
7
|
-
import { compareAddress } from '../../../utils';
|
|
7
|
+
import { compareAddress, findMarketCollection } from '../../../utils';
|
|
8
8
|
import {
|
|
9
9
|
buildQueryOptions,
|
|
10
10
|
getMarketplaceClient,
|
|
@@ -50,15 +50,18 @@ export async function fetchMarketCurrencies(
|
|
|
50
50
|
marketplaceConfigOptions(config),
|
|
51
51
|
);
|
|
52
52
|
|
|
53
|
-
const currenciesOptions =
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const currenciesOptions = findMarketCollection(
|
|
54
|
+
marketplaceConfig.market.collections,
|
|
55
|
+
collectionAddress,
|
|
56
|
+
chainId,
|
|
56
57
|
)?.currencyOptions;
|
|
57
58
|
|
|
58
59
|
// Filter currencies based on collection currency options
|
|
59
60
|
if (currenciesOptions) {
|
|
60
61
|
currencies = currencies.filter((currency) =>
|
|
61
|
-
currenciesOptions.
|
|
62
|
+
currenciesOptions.some((option) =>
|
|
63
|
+
compareAddress(option, currency.contractAddress),
|
|
64
|
+
),
|
|
62
65
|
);
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
import { ContractType, MetadataStatus } from '@0xsequence/api-client';
|
|
8
8
|
import { isAddress } from 'viem';
|
|
9
9
|
import type { Page } from '../../../types';
|
|
10
|
-
import {
|
|
10
|
+
import { findMarketCollection } from '../../../utils';
|
|
11
11
|
import {
|
|
12
12
|
buildQueryOptions,
|
|
13
13
|
getQueryClient,
|
|
@@ -148,8 +148,10 @@ export async function fetchInventory(
|
|
|
148
148
|
|
|
149
149
|
const marketCollections = marketplaceConfig?.market.collections || [];
|
|
150
150
|
|
|
151
|
-
const isMarketCollection =
|
|
152
|
-
|
|
151
|
+
const isMarketCollection = !!findMarketCollection(
|
|
152
|
+
marketCollections,
|
|
153
|
+
collectionAddress,
|
|
154
|
+
chainId,
|
|
153
155
|
);
|
|
154
156
|
|
|
155
157
|
// Determine if this collection is tradable (market collection vs shop collection)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GetFiltersArgs, PropertyFilter } from '@0xsequence/api-client';
|
|
2
2
|
import { isAddress } from 'viem';
|
|
3
3
|
import { FilterCondition } from '../../../types';
|
|
4
|
-
import {
|
|
4
|
+
import { findMarketCollection } from '../../../utils';
|
|
5
5
|
import {
|
|
6
6
|
buildQueryOptions,
|
|
7
7
|
getMetadataClient,
|
|
@@ -49,8 +49,10 @@ export async function fetchFilters(
|
|
|
49
49
|
const marketplaceConfig = await queryClient.fetchQuery(
|
|
50
50
|
marketplaceConfigOptions(config),
|
|
51
51
|
);
|
|
52
|
-
const collectionFilters =
|
|
53
|
-
|
|
52
|
+
const collectionFilters = findMarketCollection(
|
|
53
|
+
marketplaceConfig.market.collections,
|
|
54
|
+
collectionAddress,
|
|
55
|
+
chainId,
|
|
54
56
|
)?.filterSettings;
|
|
55
57
|
|
|
56
58
|
const filterOrder = collectionFilters?.filterOrder;
|
|
@@ -10,12 +10,8 @@ import {
|
|
|
10
10
|
Text,
|
|
11
11
|
} from '@0xsequence/design-system';
|
|
12
12
|
import { TrailsWidget } from '0xtrails/widget';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
getSequenceIndexerUrl,
|
|
16
|
-
getSequenceNodeGatewayUrl,
|
|
17
|
-
getTrailsApiUrl,
|
|
18
|
-
} from '../../../../_internal/api/services';
|
|
13
|
+
import { useRef, useState } from 'react';
|
|
14
|
+
import { cn } from '../../../../ssr';
|
|
19
15
|
import { ModalInitializationError } from '../../_internal/components/baseModal/errors/ModalInitializationError';
|
|
20
16
|
import { MODAL_OVERLAY_PROPS } from '../../_internal/components/consts';
|
|
21
17
|
import { useBuyModalContext } from '../internal/buyModalContext';
|
|
@@ -26,7 +22,6 @@ import { TRAILS_CUSTOM_CSS } from './TrailsCss';
|
|
|
26
22
|
|
|
27
23
|
export const BuyModalContent = () => {
|
|
28
24
|
const {
|
|
29
|
-
config,
|
|
30
25
|
modalProps,
|
|
31
26
|
close,
|
|
32
27
|
steps,
|
|
@@ -34,10 +29,12 @@ export const BuyModalContent = () => {
|
|
|
34
29
|
marketOrder,
|
|
35
30
|
collectible,
|
|
36
31
|
buyStep,
|
|
32
|
+
trailsDestination,
|
|
37
33
|
isLoading,
|
|
38
34
|
collection,
|
|
39
35
|
checkoutMode,
|
|
40
36
|
formattedAmount,
|
|
37
|
+
isMarket,
|
|
41
38
|
isShop,
|
|
42
39
|
handleTrailsSuccess,
|
|
43
40
|
handleTransactionSuccess,
|
|
@@ -48,20 +45,15 @@ export const BuyModalContent = () => {
|
|
|
48
45
|
? primarySaleItem?.currencyAddress
|
|
49
46
|
: marketOrder?.priceCurrencyAddress;
|
|
50
47
|
|
|
51
|
-
const trailsApiUrl = getTrailsApiUrl(config);
|
|
52
|
-
const sequenceIndexerUrl = getSequenceIndexerUrl(config);
|
|
53
|
-
const sequenceNodeGatewayUrl = getSequenceNodeGatewayUrl(config);
|
|
54
|
-
const sequenceApiUrl = getSequenceApiUrl(config);
|
|
55
|
-
|
|
56
48
|
if (error) {
|
|
57
49
|
return (
|
|
58
50
|
<Modal
|
|
59
|
-
isDismissible={
|
|
51
|
+
isDismissible={true}
|
|
60
52
|
onClose={close}
|
|
61
53
|
overlayProps={MODAL_OVERLAY_PROPS}
|
|
62
54
|
contentProps={{
|
|
63
55
|
style: {
|
|
64
|
-
width: '
|
|
56
|
+
width: '400px',
|
|
65
57
|
height: 'auto',
|
|
66
58
|
},
|
|
67
59
|
className: 'overflow-y-auto',
|
|
@@ -101,10 +93,7 @@ export const BuyModalContent = () => {
|
|
|
101
93
|
|
|
102
94
|
{isLoading && (
|
|
103
95
|
<div className="flex w-full items-center justify-center py-8">
|
|
104
|
-
<
|
|
105
|
-
<Spinner size="lg" />
|
|
106
|
-
<Text className="text-text-80">Loading payment options...</Text>
|
|
107
|
-
</div>
|
|
96
|
+
<ProgressiveLoadingMessage />
|
|
108
97
|
</div>
|
|
109
98
|
)}
|
|
110
99
|
|
|
@@ -123,6 +112,7 @@ export const BuyModalContent = () => {
|
|
|
123
112
|
{!isLoading &&
|
|
124
113
|
checkoutMode === 'trails' &&
|
|
125
114
|
buyStep &&
|
|
115
|
+
(!isMarket || trailsDestination) &&
|
|
126
116
|
!(isShop && primarySaleItem?.priceAmount === 0n) && (
|
|
127
117
|
<div className="pointer-events-auto w-full">
|
|
128
118
|
{collectible && (
|
|
@@ -133,25 +123,37 @@ export const BuyModalContent = () => {
|
|
|
133
123
|
/>
|
|
134
124
|
)}
|
|
135
125
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
126
|
+
{isMarket && trailsDestination ? (
|
|
127
|
+
<TrailsWidget
|
|
128
|
+
key={`market-${marketOrder?.orderId}-${trailsDestination.destinationCalldata}`}
|
|
129
|
+
toChainId={modalProps.chainId}
|
|
130
|
+
toAddress={trailsDestination.recipient}
|
|
131
|
+
toToken={trailsDestination.paymentTokenAddress}
|
|
132
|
+
toAmount={formattedAmount}
|
|
133
|
+
toCalldata={trailsDestination.destinationCalldata}
|
|
134
|
+
renderInline={true}
|
|
135
|
+
theme="dark"
|
|
136
|
+
mode="pay"
|
|
137
|
+
customCss={TRAILS_CUSTOM_CSS}
|
|
138
|
+
onDestinationConfirmation={handleTrailsSuccess}
|
|
139
|
+
payMessage="{TO_TOKEN_IMAGE}{TO_AMOUNT}{TO_TOKEN_SYMBOL}{TO_AMOUNT_USD}"
|
|
140
|
+
/>
|
|
141
|
+
) : (
|
|
142
|
+
<TrailsWidget
|
|
143
|
+
key={`direct-${buyStep.to}-${buyStep.data}`}
|
|
144
|
+
toChainId={modalProps.chainId}
|
|
145
|
+
toAddress={buyStep.to}
|
|
146
|
+
toToken={currencyAddress}
|
|
147
|
+
toAmount={formattedAmount}
|
|
148
|
+
toCalldata={buyStep.data}
|
|
149
|
+
renderInline={true}
|
|
150
|
+
theme="dark"
|
|
151
|
+
mode="pay"
|
|
152
|
+
customCss={TRAILS_CUSTOM_CSS}
|
|
153
|
+
onDestinationConfirmation={handleTrailsSuccess}
|
|
154
|
+
payMessage="{TO_TOKEN_IMAGE}{TO_AMOUNT}{TO_TOKEN_SYMBOL}{TO_AMOUNT_USD}"
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
155
157
|
</div>
|
|
156
158
|
)}
|
|
157
159
|
</div>
|
|
@@ -159,3 +161,38 @@ export const BuyModalContent = () => {
|
|
|
159
161
|
</Dialog>
|
|
160
162
|
);
|
|
161
163
|
};
|
|
164
|
+
|
|
165
|
+
const ProgressiveLoadingMessage = () => {
|
|
166
|
+
const [showSecondaryMessage, setShowSecondaryMessage] = useState(false);
|
|
167
|
+
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
|
168
|
+
|
|
169
|
+
if (!timerRef.current) {
|
|
170
|
+
timerRef.current = setTimeout(() => {
|
|
171
|
+
setShowSecondaryMessage(true);
|
|
172
|
+
}, 3000);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div className="flex items-center gap-4">
|
|
177
|
+
<div
|
|
178
|
+
className={cn(
|
|
179
|
+
'transition-all duration-300',
|
|
180
|
+
showSecondaryMessage ? 'h-10 w-10' : 'h-5 w-5',
|
|
181
|
+
)}
|
|
182
|
+
>
|
|
183
|
+
<Spinner className="h-full w-full transition-all duration-150" />
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div className="flex flex-col gap-2">
|
|
187
|
+
<p className="animate-pulse text-text-100">
|
|
188
|
+
Loading payment options...
|
|
189
|
+
</p>
|
|
190
|
+
{showSecondaryMessage && (
|
|
191
|
+
<p className="text-small text-text-50">
|
|
192
|
+
This is taking longer than expected.
|
|
193
|
+
</p>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Button, Spinner, Text } from '@0xsequence/design-system';
|
|
4
|
-
import { useState } from 'react';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
5
|
import type { Hex } from 'viem';
|
|
6
6
|
import { getPresentableChainName } from '../../../../../utils/network';
|
|
7
7
|
import type { Step } from '../../../../_internal';
|
|
@@ -39,13 +39,36 @@ export const CryptoPaymentModal = ({
|
|
|
39
39
|
} = useCryptoPaymentModalContext({ chainId, steps, onSuccess });
|
|
40
40
|
|
|
41
41
|
const { ensureCorrectChainAsync } = useEnsureCorrectChain();
|
|
42
|
+
const [pendingAction, setPendingAction] = useState<'approval' | 'buy' | null>(
|
|
43
|
+
null,
|
|
44
|
+
);
|
|
42
45
|
const [chainSwitchError, setChainSwitchError] = useState<{
|
|
43
46
|
title: string;
|
|
44
47
|
message: string;
|
|
45
48
|
details?: Error;
|
|
46
49
|
} | null>(null);
|
|
50
|
+
const isPendingApprovalReady =
|
|
51
|
+
pendingAction === 'approval' && isOnCorrectChain && canApprove;
|
|
52
|
+
const isPendingBuyReady = pendingAction === 'buy' && isOnCorrectChain && canBuy;
|
|
53
|
+
const isPendingAction = pendingAction !== null;
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!isPendingApprovalReady && !isPendingBuyReady) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
setPendingAction(null);
|
|
61
|
+
|
|
62
|
+
void (pendingAction === 'approval' ? executeApproval() : executeBuy());
|
|
63
|
+
}, [
|
|
64
|
+
executeApproval,
|
|
65
|
+
executeBuy,
|
|
66
|
+
isPendingApprovalReady,
|
|
67
|
+
isPendingBuyReady,
|
|
68
|
+
pendingAction,
|
|
69
|
+
]);
|
|
47
70
|
|
|
48
|
-
const handleChainSwitchError = (error
|
|
71
|
+
const handleChainSwitchError = (error?: Error) => {
|
|
49
72
|
const chainName = getPresentableChainName(chainId);
|
|
50
73
|
setChainSwitchError({
|
|
51
74
|
title: 'Chain switch failed',
|
|
@@ -58,27 +81,59 @@ export const CryptoPaymentModal = ({
|
|
|
58
81
|
setChainSwitchError(null);
|
|
59
82
|
};
|
|
60
83
|
|
|
84
|
+
const executeAction = async (action: 'approval' | 'buy') => {
|
|
85
|
+
setPendingAction(null);
|
|
86
|
+
|
|
87
|
+
if (action === 'approval') {
|
|
88
|
+
await executeApproval();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await executeBuy();
|
|
93
|
+
};
|
|
94
|
+
|
|
61
95
|
const executeWithChainSwitch = async (action: 'approval' | 'buy') => {
|
|
62
96
|
dismissChainSwitchError();
|
|
97
|
+
|
|
98
|
+
if (isOnCorrectChain) {
|
|
99
|
+
await executeAction(action);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setPendingAction(action);
|
|
104
|
+
|
|
63
105
|
try {
|
|
64
|
-
await ensureCorrectChainAsync(chainId);
|
|
106
|
+
const didRequestChainSwitch = await ensureCorrectChainAsync(chainId);
|
|
107
|
+
if (!didRequestChainSwitch) {
|
|
108
|
+
setPendingAction(null);
|
|
109
|
+
handleChainSwitchError();
|
|
110
|
+
}
|
|
65
111
|
} catch (error) {
|
|
112
|
+
setPendingAction(null);
|
|
113
|
+
|
|
66
114
|
if (error instanceof Error) {
|
|
67
115
|
handleChainSwitchError(error);
|
|
116
|
+
} else {
|
|
117
|
+
handleChainSwitchError();
|
|
68
118
|
}
|
|
69
119
|
}
|
|
70
|
-
|
|
71
|
-
if (action === 'approval') {
|
|
72
|
-
await executeApproval();
|
|
73
|
-
} else {
|
|
74
|
-
await executeBuy();
|
|
75
|
-
}
|
|
76
120
|
};
|
|
77
121
|
|
|
122
|
+
const isWaitingForChainSwitch = isPendingAction && !isOnCorrectChain;
|
|
123
|
+
const isPreparingPendingAction = isPendingAction && isOnCorrectChain;
|
|
124
|
+
|
|
78
125
|
const approvalButtonLabel = isApproving ? (
|
|
79
126
|
<div className="flex items-center gap-2">
|
|
80
127
|
<Spinner size="sm" /> Approving Token...
|
|
81
128
|
</div>
|
|
129
|
+
) : isWaitingForChainSwitch && pendingAction === 'approval' ? (
|
|
130
|
+
<div className="flex items-center gap-2">
|
|
131
|
+
<Spinner size="sm" /> Switching Network...
|
|
132
|
+
</div>
|
|
133
|
+
) : isPreparingPendingAction && pendingAction === 'approval' ? (
|
|
134
|
+
<div className="flex items-center gap-2">
|
|
135
|
+
<Spinner size="sm" /> Preparing Approval...
|
|
136
|
+
</div>
|
|
82
137
|
) : (
|
|
83
138
|
'Approve Token'
|
|
84
139
|
);
|
|
@@ -87,6 +142,14 @@ export const CryptoPaymentModal = ({
|
|
|
87
142
|
<div className="flex items-center gap-2">
|
|
88
143
|
<Spinner size="sm" /> Confirming Purchase...
|
|
89
144
|
</div>
|
|
145
|
+
) : isWaitingForChainSwitch && pendingAction === 'buy' ? (
|
|
146
|
+
<div className="flex items-center gap-2">
|
|
147
|
+
<Spinner size="sm" /> Switching Network...
|
|
148
|
+
</div>
|
|
149
|
+
) : isPreparingPendingAction && pendingAction === 'buy' ? (
|
|
150
|
+
<div className="flex items-center gap-2">
|
|
151
|
+
<Spinner size="sm" /> Preparing Purchase...
|
|
152
|
+
</div>
|
|
90
153
|
) : (
|
|
91
154
|
'Buy now'
|
|
92
155
|
);
|
|
@@ -129,7 +192,7 @@ export const CryptoPaymentModal = ({
|
|
|
129
192
|
onClick={async () => {
|
|
130
193
|
await executeWithChainSwitch('approval');
|
|
131
194
|
}}
|
|
132
|
-
disabled={!canApprove}
|
|
195
|
+
disabled={!canApprove || isPendingAction}
|
|
133
196
|
variant="primary"
|
|
134
197
|
size="lg"
|
|
135
198
|
className="w-full"
|
|
@@ -143,7 +206,7 @@ export const CryptoPaymentModal = ({
|
|
|
143
206
|
onClick={async () => {
|
|
144
207
|
await executeWithChainSwitch('buy');
|
|
145
208
|
}}
|
|
146
|
-
disabled={!canBuy}
|
|
209
|
+
disabled={!canBuy || isPendingAction}
|
|
147
210
|
variant="primary"
|
|
148
211
|
size="lg"
|
|
149
212
|
className="w-full"
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
type WagmiAdapterOptions,
|
|
5
|
+
wagmiAdapter,
|
|
6
|
+
} from '@0xtrails/adapter-wagmi';
|
|
7
|
+
import { TrailsProvider } from '0xtrails/widget';
|
|
8
|
+
import { useConfig as useWagmiConfig } from 'wagmi';
|
|
9
|
+
import {
|
|
10
|
+
getSequenceApiUrl,
|
|
11
|
+
getSequenceIndexerUrl,
|
|
12
|
+
getSequenceNodeGatewayUrl,
|
|
13
|
+
getTrailsApiUrl,
|
|
14
|
+
} from '../../../../_internal/api/services';
|
|
15
|
+
import { useConfig } from '../../../../hooks';
|
|
16
|
+
import { useWaasFeeOptions } from '../../../../hooks/utils/useWaasFeeOptions';
|
|
3
17
|
import { useIsOpen } from '../store';
|
|
4
18
|
import { BuyModalContent } from './BuyModalContent';
|
|
5
19
|
|
|
20
|
+
type TrailsUseWaasFeeOptions = NonNullable<
|
|
21
|
+
NonNullable<WagmiAdapterOptions['sequence']>['useWaasFeeOptions']
|
|
22
|
+
>;
|
|
23
|
+
|
|
6
24
|
export const BuyModal = () => {
|
|
7
25
|
const isOpen = useIsOpen();
|
|
8
26
|
|
|
@@ -10,5 +28,48 @@ export const BuyModal = () => {
|
|
|
10
28
|
return null;
|
|
11
29
|
}
|
|
12
30
|
|
|
13
|
-
return <
|
|
31
|
+
return <BuyModalWithTrailsProvider />;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const useTrailsWaasFeeOptions: TrailsUseWaasFeeOptions = ({
|
|
35
|
+
chainIdOverride,
|
|
36
|
+
} = {}) => {
|
|
37
|
+
const config = useConfig();
|
|
38
|
+
const {
|
|
39
|
+
pendingFeeOptionConfirmation,
|
|
40
|
+
confirmPendingFeeOption,
|
|
41
|
+
rejectPendingFeeOption,
|
|
42
|
+
} = useWaasFeeOptions(chainIdOverride ?? 0, config);
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
pendingFeeOptionConfirmation,
|
|
46
|
+
confirmPendingFeeOption,
|
|
47
|
+
rejectPendingFeeOption,
|
|
48
|
+
];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const BuyModalWithTrailsProvider = () => {
|
|
52
|
+
const config = useConfig();
|
|
53
|
+
const wagmiConfig = useWagmiConfig();
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<TrailsProvider
|
|
57
|
+
config={{
|
|
58
|
+
trailsApiKey: config.projectAccessKey,
|
|
59
|
+
trailsApiUrl: getTrailsApiUrl(config),
|
|
60
|
+
sequenceIndexerUrl: getSequenceIndexerUrl(config),
|
|
61
|
+
sequenceNodeGatewayUrl: getSequenceNodeGatewayUrl(config),
|
|
62
|
+
sequenceApiUrl: getSequenceApiUrl(config),
|
|
63
|
+
walletConnectProjectId: config.walletConnectProjectId,
|
|
64
|
+
adapters: [
|
|
65
|
+
wagmiAdapter({
|
|
66
|
+
wagmiConfig,
|
|
67
|
+
sequence: { useWaasFeeOptions: useTrailsWaasFeeOptions },
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<BuyModalContent />
|
|
73
|
+
</TrailsProvider>
|
|
74
|
+
);
|
|
14
75
|
};
|
|
@@ -5,7 +5,6 @@ import type { Hex } from 'viem';
|
|
|
5
5
|
import { useAccount, usePublicClient, useWalletClient } from 'wagmi';
|
|
6
6
|
import { useConfig } from '../../../..';
|
|
7
7
|
import { getIndexerClient, type Step } from '../../../../_internal';
|
|
8
|
-
import { useBuyModalData } from './useBuyModalData';
|
|
9
8
|
|
|
10
9
|
// https://github.com/0xsequence/web-sdk/blob/620b6fe7681ae49efd4eb3fa7607ef01dd7ede54/packages/connect/src/utils/transactions.ts#L11-L19
|
|
11
10
|
class FeeOptionInsufficientFundsError extends Error {
|
|
@@ -21,31 +20,25 @@ class FeeOptionInsufficientFundsError extends Error {
|
|
|
21
20
|
type UseExecuteBundledTransactions = {
|
|
22
21
|
chainId: number;
|
|
23
22
|
approvalStep?: Step;
|
|
24
|
-
priceAmount: bigint;
|
|
25
23
|
};
|
|
26
24
|
|
|
27
25
|
const useExecuteBundledTransactions = ({
|
|
28
26
|
chainId,
|
|
29
27
|
approvalStep,
|
|
30
|
-
priceAmount,
|
|
31
28
|
}: UseExecuteBundledTransactions) => {
|
|
32
29
|
const config = useConfig();
|
|
33
30
|
const [isExecuting, setIsExecuting] = useState(false);
|
|
34
31
|
const { address, connector } = useAccount();
|
|
35
|
-
const publicClient = usePublicClient();
|
|
32
|
+
const publicClient = usePublicClient({ chainId });
|
|
36
33
|
const { data: walletClient } = useWalletClient();
|
|
37
34
|
const indexerClient = getIndexerClient(chainId, config);
|
|
38
35
|
|
|
39
|
-
const { collection, currency } = useBuyModalData();
|
|
40
|
-
|
|
41
36
|
const isReady =
|
|
42
37
|
!!address &&
|
|
43
38
|
!!publicClient &&
|
|
44
39
|
!!walletClient &&
|
|
45
40
|
!!indexerClient &&
|
|
46
|
-
!!connector
|
|
47
|
-
!!collection?.address &&
|
|
48
|
-
priceAmount != null;
|
|
41
|
+
!!connector;
|
|
49
42
|
|
|
50
43
|
const executeBundledTransactions = async ({
|
|
51
44
|
step,
|
|
@@ -79,28 +72,22 @@ const useExecuteBundledTransactions = ({
|
|
|
79
72
|
throw new Error('Connector not found');
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
75
|
+
const buildTransaction = (step: Step) => {
|
|
76
|
+
const value = step.value ? BigInt(step.value) : 0n;
|
|
85
77
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
return {
|
|
79
|
+
to: step.to,
|
|
80
|
+
data: step.data as Hex,
|
|
81
|
+
chainId,
|
|
82
|
+
...(value > 0n ? { value } : {}),
|
|
83
|
+
};
|
|
84
|
+
};
|
|
89
85
|
|
|
90
86
|
const approvalData = approvalStep
|
|
91
|
-
?
|
|
92
|
-
to: approvalStep.to,
|
|
93
|
-
data: approvalStep.data as Hex,
|
|
94
|
-
chainId,
|
|
95
|
-
}
|
|
87
|
+
? buildTransaction(approvalStep)
|
|
96
88
|
: undefined;
|
|
97
89
|
|
|
98
|
-
const transactionData =
|
|
99
|
-
to: step.to,
|
|
100
|
-
data: step.data as Hex,
|
|
101
|
-
chainId,
|
|
102
|
-
...(currency?.nativeCurrency ? { value: priceAmount } : {}),
|
|
103
|
-
};
|
|
90
|
+
const transactionData = buildTransaction(step);
|
|
104
91
|
|
|
105
92
|
const transactions = [
|
|
106
93
|
...(approvalData ? [approvalData] : []),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { skipToken } from '@tanstack/react-query';
|
|
2
2
|
import { avalanche, optimism } from 'viem/chains';
|
|
3
|
-
import {
|
|
3
|
+
import { findMarketCollection } from '../../../../../utils';
|
|
4
4
|
import type { AdditionalFee } from '../../../../_internal';
|
|
5
5
|
import { useMarketplaceConfig } from '../../../../hooks';
|
|
6
6
|
|
|
@@ -28,10 +28,10 @@ export const useMarketPlatformFee = (params: FeesParams | typeof skipToken) => {
|
|
|
28
28
|
const { chainId, collectionAddress } = params;
|
|
29
29
|
|
|
30
30
|
// Find collection in market collections only (not shop collections)
|
|
31
|
-
const marketCollection =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const marketCollection = findMarketCollection(
|
|
32
|
+
marketplaceConfig?.market?.collections ?? [],
|
|
33
|
+
collectionAddress,
|
|
34
|
+
chainId,
|
|
35
35
|
);
|
|
36
36
|
|
|
37
37
|
const avalancheOrOptimism =
|