@0xsequence/marketplace-sdk 0.3.0 → 0.3.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.
Files changed (165) hide show
  1. package/dist/chunk-22NLQ3AS.js +3078 -0
  2. package/dist/chunk-22NLQ3AS.js.map +1 -0
  3. package/dist/chunk-3OHM45R3.js +1294 -0
  4. package/dist/chunk-3OHM45R3.js.map +1 -0
  5. package/dist/{chunk-MQR6SHXZ.js → chunk-4YU2UPYH.js} +58 -103
  6. package/dist/chunk-4YU2UPYH.js.map +1 -0
  7. package/dist/chunk-7NJETFMF.js +21 -0
  8. package/dist/chunk-7NJETFMF.js.map +1 -0
  9. package/dist/{chunk-UYRQ5MJQ.js → chunk-FUM4OGOQ.js} +4 -4
  10. package/dist/chunk-FUM4OGOQ.js.map +1 -0
  11. package/dist/{chunk-7OO74L2K.js → chunk-GJAKQ5Q3.js} +40 -1
  12. package/dist/chunk-GJAKQ5Q3.js.map +1 -0
  13. package/dist/chunk-MCI3KOSQ.js +2 -0
  14. package/dist/{chunk-BJE7AG6V.js → chunk-O7UQGT43.js} +698 -23
  15. package/dist/chunk-O7UQGT43.js.map +1 -0
  16. package/dist/{chunk-CSN7YD5Q.js → chunk-Q57TEA3Z.js} +20 -2
  17. package/dist/chunk-Q57TEA3Z.js.map +1 -0
  18. package/dist/{chunk-VEX7FDL6.js → chunk-SBVLWSRZ.js} +2 -2
  19. package/dist/{chunk-VEX7FDL6.js.map → chunk-SBVLWSRZ.js.map} +1 -1
  20. package/dist/{chunk-6S4FYXP6.js → chunk-SPW24Y7I.js} +40 -1
  21. package/dist/chunk-SPW24Y7I.js.map +1 -0
  22. package/dist/chunk-UISBTKFF.js +1 -0
  23. package/dist/{chunk-OUWB3FHZ.js → chunk-WA433WAJ.js} +9 -33
  24. package/dist/chunk-WA433WAJ.js.map +1 -0
  25. package/dist/{chunk-O5JXKTWP.js → chunk-WFE6OCYF.js} +4 -4
  26. package/dist/chunk-WFE6OCYF.js.map +1 -0
  27. package/dist/chunk-XX4EVWBF.js +1292 -0
  28. package/dist/chunk-XX4EVWBF.js.map +1 -0
  29. package/dist/chunk-Y7YO5TLE.js +53 -0
  30. package/dist/chunk-Y7YO5TLE.js.map +1 -0
  31. package/dist/index.css +1 -50
  32. package/dist/index.d.ts +3 -5
  33. package/dist/index.js +12 -14
  34. package/dist/index.js.map +1 -1
  35. package/dist/react/hooks/index.css +82 -0
  36. package/dist/react/hooks/index.css.map +1 -0
  37. package/dist/react/hooks/index.d.ts +401 -462
  38. package/dist/react/hooks/index.js +26 -6
  39. package/dist/react/index.css +56 -91
  40. package/dist/react/index.css.map +1 -1
  41. package/dist/react/index.d.ts +2 -2
  42. package/dist/react/index.js +32 -13
  43. package/dist/react/ui/components/index.css +86 -121
  44. package/dist/react/ui/components/index.css.map +1 -1
  45. package/dist/react/ui/components/index.d.ts +10 -4
  46. package/dist/react/ui/components/index.js +12 -11
  47. package/dist/react/ui/icons/index.js +3 -2
  48. package/dist/react/ui/icons/index.js.map +1 -1
  49. package/dist/react/ui/index.css +56 -91
  50. package/dist/react/ui/index.css.map +1 -1
  51. package/dist/react/ui/index.d.ts +29 -31
  52. package/dist/react/ui/index.js +14 -11
  53. package/dist/react/ui/modals/_internal/components/actionModal/index.js +5 -16
  54. package/dist/react/ui/modals/_internal/components/actionModal/index.js.map +1 -1
  55. package/dist/styles/index.css +1 -50
  56. package/dist/styles/index.css.map +1 -1
  57. package/dist/styles/index.d.ts +2 -6
  58. package/dist/styles/index.js +9 -11
  59. package/dist/utils/abi/index.d.ts +2 -0
  60. package/dist/utils/abi/index.js +21 -0
  61. package/dist/utils/abi/marketplace/index.d.ts +805 -0
  62. package/dist/utils/abi/marketplace/index.js +12 -0
  63. package/dist/utils/abi/{abi/token → token}/index.js +1 -2
  64. package/dist/utils/index.d.ts +5 -5
  65. package/dist/utils/index.js +12 -14
  66. package/package.json +17 -15
  67. package/src/react/_internal/transaction-machine/execute-transaction.ts +592 -0
  68. package/src/react/_internal/transaction-machine/useTransactionMachine.ts +66 -0
  69. package/src/react/hooks/index.ts +4 -0
  70. package/src/react/hooks/useBuyCollectable.tsx +38 -0
  71. package/src/react/hooks/useCancelOrder.tsx +38 -0
  72. package/src/react/hooks/useCheckoutOptions.tsx +9 -6
  73. package/src/react/hooks/useCreateListing.tsx +65 -0
  74. package/src/react/hooks/useGenerateBuyTransaction.tsx +71 -0
  75. package/src/react/hooks/useListListingsForCollectible.tsx +1 -1
  76. package/src/react/hooks/useMakeOffer.tsx +62 -0
  77. package/src/react/hooks/useRoyaltyPercentage.tsx +1 -1
  78. package/src/react/hooks/useSell.tsx +62 -0
  79. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +107 -115
  80. package/src/react/ui/components/_internals/custom-select/CustomSelect.tsx +63 -0
  81. package/src/react/ui/components/_internals/custom-select/styles.css.ts +64 -0
  82. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +127 -130
  83. package/src/react/ui/components/collectible-card/Footer.tsx +65 -58
  84. package/src/react/ui/icons/Bell.tsx +2 -2
  85. package/src/react/ui/index.ts +1 -0
  86. package/src/react/ui/modals/BuyModal/_store.ts +53 -0
  87. package/src/react/ui/modals/BuyModal/index.tsx +119 -0
  88. package/src/react/ui/modals/CreateListingModal/_store.ts +35 -312
  89. package/src/react/ui/modals/CreateListingModal/index.tsx +185 -126
  90. package/src/react/ui/modals/MakeOfferModal/_store.ts +34 -276
  91. package/src/react/ui/modals/MakeOfferModal/index.tsx +195 -136
  92. package/src/react/ui/modals/SellModal/_store.ts +29 -262
  93. package/src/react/ui/modals/SellModal/index.tsx +156 -121
  94. package/src/react/ui/modals/SuccessfulPurchaseModal/_store.ts +17 -3
  95. package/src/react/ui/modals/SuccessfulPurchaseModal/index.tsx +3 -2
  96. package/src/react/ui/modals/TransferModal/index.tsx +9 -16
  97. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +1 -0
  98. package/src/react/ui/modals/_internal/components/actionModal/ErrorModal.tsx +18 -0
  99. package/src/react/ui/modals/_internal/components/actionModal/LoadingModal.tsx +18 -0
  100. package/src/react/ui/modals/_internal/components/calendarPopover/index.tsx +1 -0
  101. package/src/react/ui/modals/_internal/components/calendarPopover/overrides.css +8 -0
  102. package/src/react/ui/modals/_internal/components/calendarPopover/styles.css.ts +10 -4
  103. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +10 -13
  104. package/src/react/ui/modals/_internal/components/expirationDateSelect/index.tsx +23 -9
  105. package/src/react/ui/modals/_internal/components/priceInput/index.tsx +7 -18
  106. package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +17 -44
  107. package/src/react/ui/modals/_internal/components/switchChainModal/store.ts +10 -8
  108. package/src/react/ui/modals/_internal/components/tokenPreview/index.tsx +14 -3
  109. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +4 -2
  110. package/src/react/ui/modals/_internal/components/transactionHeader/index.tsx +4 -4
  111. package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +4 -2
  112. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +13 -10
  113. package/src/react/ui/modals/_internal/types.ts +13 -0
  114. package/src/react/ui/modals/modal-provider.tsx +4 -2
  115. package/src/styles/index.ts +0 -2
  116. package/src/utils/abi/index.ts +2 -0
  117. package/src/utils/abi/marketplace/index.ts +3 -0
  118. package/src/utils/abi/marketplace/sequence-marketplace-v1.ts +463 -0
  119. package/src/utils/abi/marketplace/sequence-marketplace-v2.ts +802 -0
  120. package/src/utils/index.ts +2 -3
  121. package/src/utils/network.ts +4 -2
  122. package/tsconfig.tsbuildinfo +1 -1
  123. package/dist/chunk-6JWGELXL.js +0 -214
  124. package/dist/chunk-6JWGELXL.js.map +0 -1
  125. package/dist/chunk-6S4FYXP6.js.map +0 -1
  126. package/dist/chunk-7OO74L2K.js.map +0 -1
  127. package/dist/chunk-BCNFYVAL.js +0 -1
  128. package/dist/chunk-BJE7AG6V.js.map +0 -1
  129. package/dist/chunk-CSN7YD5Q.js.map +0 -1
  130. package/dist/chunk-D7QQP6MS.js +0 -2
  131. package/dist/chunk-DBFOPEV6.js +0 -23
  132. package/dist/chunk-DBFOPEV6.js.map +0 -1
  133. package/dist/chunk-MQR6SHXZ.js.map +0 -1
  134. package/dist/chunk-O5JXKTWP.js.map +0 -1
  135. package/dist/chunk-OUWB3FHZ.js.map +0 -1
  136. package/dist/chunk-PE2LLUTJ.js +0 -213
  137. package/dist/chunk-PE2LLUTJ.js.map +0 -1
  138. package/dist/chunk-QVFMD6S2.js +0 -21
  139. package/dist/chunk-QVFMD6S2.js.map +0 -1
  140. package/dist/chunk-QXLZPSSR.js +0 -3316
  141. package/dist/chunk-QXLZPSSR.js.map +0 -1
  142. package/dist/chunk-UYRQ5MJQ.js.map +0 -1
  143. package/dist/utils/abi/abi/standard/index.d.ts +0 -25
  144. package/dist/utils/abi/abi/standard/index.js +0 -8
  145. package/dist/utils/abi/clients/index.d.ts +0 -27
  146. package/dist/utils/abi/clients/index.js +0 -13
  147. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/styles.css.ts +0 -33
  148. package/src/react/ui/modals/_internal/components/expirationDateSelect/styles.css.ts +0 -25
  149. package/src/utils/abi/abi/standard/index.ts +0 -1
  150. package/src/utils/abi/clients/ERC1155.ts +0 -82
  151. package/src/utils/abi/clients/ERC20.ts +0 -101
  152. package/src/utils/abi/clients/ERC721.ts +0 -97
  153. package/src/utils/abi/clients/index.ts +0 -3
  154. /package/dist/{chunk-BCNFYVAL.js.map → chunk-MCI3KOSQ.js.map} +0 -0
  155. /package/dist/{chunk-D7QQP6MS.js.map → chunk-UISBTKFF.js.map} +0 -0
  156. /package/dist/utils/abi/{abi/standard/index.js.map → index.js.map} +0 -0
  157. /package/dist/utils/abi/{abi/token → marketplace}/index.js.map +0 -0
  158. /package/dist/utils/abi/{abi/token → token}/index.d.ts +0 -0
  159. /package/dist/utils/abi/{clients → token}/index.js.map +0 -0
  160. /package/src/react/hooks/{useGenerateCancleTransaction.tsx → useGenerateCancelTransaction.tsx} +0 -0
  161. /package/src/utils/abi/{abi/standard → marketplace}/EIP2981.ts +0 -0
  162. /package/src/utils/abi/{abi/token → token}/ERC1155.ts +0 -0
  163. /package/src/utils/abi/{abi/token → token}/ERC20.ts +0 -0
  164. /package/src/utils/abi/{abi/token → token}/ERC721.ts +0 -0
  165. /package/src/utils/abi/{abi/token → token}/index.ts +0 -0
@@ -0,0 +1,592 @@
1
+ import type { SelectPaymentSettings } from '@0xsequence/kit-checkout';
2
+ import type {
3
+ Chain,
4
+ Hash,
5
+ Hex,
6
+ PublicClient,
7
+ TypedDataDomain,
8
+ WalletClient,
9
+ } from 'viem';
10
+ import { avalanche } from 'viem/chains';
11
+ import {
12
+ type AdditionalFee,
13
+ type SequenceMarketplace,
14
+ TransactionSwapProvider,
15
+ type WalletKind,
16
+ getMarketplaceClient,
17
+ } from '..';
18
+ import {
19
+ type ContractType,
20
+ type CreateReq,
21
+ ExecuteType,
22
+ type MarketplaceConfig,
23
+ type MarketplaceKind,
24
+ OrderbookKind,
25
+ type SdkConfig,
26
+ type Step,
27
+ StepType,
28
+ } from '../../../types';
29
+
30
+ export enum TransactionState {
31
+ IDLE = 'IDLE',
32
+ SWITCH_CHAIN = 'SWITCH_CHAIN',
33
+ CHECKING_STEPS = 'CHECKING_STEPS',
34
+ TOKEN_APPROVAL = 'TOKEN_APPROVAL',
35
+ EXECUTING_TRANSACTION = 'EXECUTING_TRANSACTION',
36
+ CONFIRMING = 'CONFIRMING',
37
+ SUCCESS = 'SUCCESS',
38
+ ERROR = 'ERROR',
39
+ }
40
+
41
+ export enum TransactionType {
42
+ BUY = 'BUY',
43
+ SELL = 'SELL',
44
+ LISTING = 'LISTING',
45
+ OFFER = 'OFFER',
46
+ CANCEL = 'CANCEL',
47
+ }
48
+
49
+ export interface TransactionConfig {
50
+ type: TransactionType;
51
+ walletKind: WalletKind;
52
+ chainId: string;
53
+ chains: readonly Chain[];
54
+ collectionAddress: string;
55
+ sdkConfig: SdkConfig;
56
+ marketplaceConfig: MarketplaceConfig;
57
+ }
58
+
59
+ interface StateConfig {
60
+ config: TransactionConfig;
61
+ onTransactionSent?: (hash: Hash) => void;
62
+ onSuccess?: (hash: Hash) => void;
63
+ onError?: (error: Error) => void;
64
+ }
65
+
66
+ export interface BuyInput {
67
+ orderId: string;
68
+ collectableDecimals: number;
69
+ marketplace: MarketplaceKind;
70
+ quantity: string;
71
+ }
72
+
73
+ export interface SellInput {
74
+ orderId: string;
75
+ marketplace: MarketplaceKind;
76
+ quantity?: string;
77
+ }
78
+
79
+ export interface ListingInput {
80
+ contractType: ContractType;
81
+ listing: CreateReq;
82
+ }
83
+
84
+ export interface OfferInput {
85
+ contractType: ContractType;
86
+ offer: CreateReq;
87
+ }
88
+
89
+ export interface CancelInput {
90
+ orderId: string;
91
+ marketplace: MarketplaceKind;
92
+ }
93
+
94
+ type TransactionInput =
95
+ | {
96
+ type: TransactionType.BUY;
97
+ props: BuyInput;
98
+ }
99
+ | {
100
+ type: TransactionType.SELL;
101
+ props: SellInput;
102
+ }
103
+ | {
104
+ type: TransactionType.LISTING;
105
+ props: ListingInput;
106
+ }
107
+ | {
108
+ type: TransactionType.OFFER;
109
+ props: OfferInput;
110
+ }
111
+ | {
112
+ type: TransactionType.CANCEL;
113
+ props: CancelInput;
114
+ };
115
+
116
+ interface StateConfig {
117
+ config: TransactionConfig;
118
+ onTransactionSent?: (hash: Hash) => void;
119
+ onSuccess?: (hash: Hash) => void;
120
+ onError?: (error: Error) => void;
121
+ }
122
+
123
+ interface TransactionStep {
124
+ isPending: boolean;
125
+ isExecuting: boolean;
126
+ }
127
+
128
+ export interface TransactionSteps {
129
+ switchChain: TransactionStep & {
130
+ execute: () => Promise<void>;
131
+ };
132
+ approval: TransactionStep & {
133
+ execute: () =>
134
+ | Promise<{ hash: Hash } | undefined>
135
+ | Promise<void>
136
+ | undefined;
137
+ };
138
+ transaction: TransactionStep & {
139
+ execute: () => Promise<{ hash: Hash } | undefined> | Promise<void>;
140
+ };
141
+ }
142
+
143
+ const debug = (message: string, data?: any) => {
144
+ console.debug(`[TransactionMachine] ${message}`, data || '');
145
+ };
146
+
147
+ export class TransactionMachine {
148
+ private currentState: TransactionState;
149
+ private marketplaceClient: SequenceMarketplace;
150
+ private memoizedSteps: TransactionSteps | null = null;
151
+ private lastProps: TransactionInput['props'] | null = null;
152
+
153
+ constructor(
154
+ private readonly config: StateConfig,
155
+ private readonly walletClient: WalletClient,
156
+ private readonly publicClient: PublicClient,
157
+ private readonly openSelectPaymentModal: (
158
+ settings: SelectPaymentSettings,
159
+ ) => void,
160
+ private readonly switchChainFn: (chainId: string) => Promise<void>,
161
+ ) {
162
+ this.currentState = TransactionState.IDLE;
163
+ this.marketplaceClient = getMarketplaceClient(
164
+ config.config.chainId,
165
+ config.config.sdkConfig,
166
+ );
167
+ }
168
+
169
+ private getAccount() {
170
+ const account = this.walletClient.account;
171
+ if (!account) {
172
+ throw new Error('Account not connected');
173
+ }
174
+ return account;
175
+ }
176
+
177
+ private getMarketplaceFee(collectionAddress: string) {
178
+ const defaultFee = 2.5;
179
+ const defaultPlatformFeeRecipient =
180
+ '0x858dB1cbF6D09D447C96A11603189b49B2D1C219';
181
+ const avalancheAndOptimismPlatformFeeRecipient =
182
+ '0x400cdab4676c17aec07e8ec748a5fc3b674bca41';
183
+ const collection = this.config.config.marketplaceConfig.collections.find(
184
+ (collection) =>
185
+ collection.collectionAddress.toLowerCase() ===
186
+ collectionAddress.toLowerCase() &&
187
+ this.getChainId() === Number(collection.chainId),
188
+ );
189
+
190
+ const receiver =
191
+ this.getChainId() === avalanche.id
192
+ ? avalancheAndOptimismPlatformFeeRecipient
193
+ : defaultPlatformFeeRecipient;
194
+
195
+ const percentageToBPS = (percentage: string | number) =>
196
+ (Number(percentage) * 10000) / 100;
197
+
198
+ return {
199
+ amount: percentageToBPS(
200
+ collection?.marketplaceFeePercentage || defaultFee,
201
+ ).toString(),
202
+ receiver,
203
+ } satisfies AdditionalFee;
204
+ }
205
+
206
+ private getAccountAddress() {
207
+ return this.getAccount().address;
208
+ }
209
+
210
+ private async generateSteps({
211
+ type,
212
+ props,
213
+ }: TransactionInput): Promise<Step[]> {
214
+ debug('Generating steps', { type, props });
215
+ const { collectionAddress } = this.config.config;
216
+ const address = this.getAccountAddress();
217
+ switch (type) {
218
+ case TransactionType.BUY:
219
+ return this.marketplaceClient
220
+ .generateBuyTransaction({
221
+ collectionAddress,
222
+ buyer: address,
223
+ walletType: this.config.config.walletKind,
224
+ marketplace: props.marketplace,
225
+ ordersData: [
226
+ {
227
+ orderId: props.orderId,
228
+ quantity: props.quantity || '1',
229
+ },
230
+ ],
231
+ additionalFees: [this.getMarketplaceFee(collectionAddress)],
232
+ })
233
+ .then((resp) => resp.steps);
234
+
235
+ case TransactionType.SELL:
236
+ return this.marketplaceClient
237
+ .generateSellTransaction({
238
+ collectionAddress,
239
+ seller: address,
240
+ walletType: this.config.config.walletKind,
241
+ marketplace: props.marketplace,
242
+ ordersData: [
243
+ {
244
+ orderId: props.orderId,
245
+ quantity: props.quantity || '1',
246
+ },
247
+ ],
248
+ additionalFees: [],
249
+ })
250
+ .then((resp) => resp.steps);
251
+
252
+ case TransactionType.LISTING:
253
+ return this.marketplaceClient
254
+ .generateListingTransaction({
255
+ collectionAddress,
256
+ owner: address,
257
+ walletType: this.config.config.walletKind,
258
+ contractType: props.contractType,
259
+ orderbook: OrderbookKind.sequence_marketplace_v2,
260
+ listing: props.listing,
261
+ })
262
+ .then((resp) => resp.steps);
263
+
264
+ case TransactionType.OFFER:
265
+ return this.marketplaceClient
266
+ .generateOfferTransaction({
267
+ collectionAddress,
268
+ maker: address,
269
+ walletType: this.config.config.walletKind,
270
+ contractType: props.contractType,
271
+ orderbook: OrderbookKind.sequence_marketplace_v2,
272
+ offer: props.offer,
273
+ })
274
+ .then((resp) => resp.steps);
275
+
276
+ case TransactionType.CANCEL:
277
+ return this.marketplaceClient
278
+ .generateCancelTransaction({
279
+ collectionAddress,
280
+ maker: address,
281
+ marketplace: props.marketplace,
282
+ orderId: props.orderId,
283
+ })
284
+ .then((resp) => resp.steps);
285
+
286
+ default:
287
+ throw new Error(`Unknown transaction type: ${type}`);
288
+ }
289
+ }
290
+
291
+ private clearMemoizedSteps() {
292
+ debug('Clearing memoized steps');
293
+ this.memoizedSteps = null;
294
+ this.lastProps = null;
295
+ }
296
+
297
+ private async transition(newState: TransactionState) {
298
+ debug(`State transition: ${this.currentState} -> ${newState}`);
299
+ this.currentState = newState;
300
+ this.clearMemoizedSteps();
301
+ }
302
+
303
+ private getChainId() {
304
+ return this.walletClient.chain?.id;
305
+ }
306
+
307
+ private getChainForTransaction() {
308
+ const chainId = this.config.config.chainId;
309
+ return this.config.config.chains.find(
310
+ (chain) => chain.id === Number(chainId),
311
+ );
312
+ }
313
+
314
+ private isOnCorrectChain() {
315
+ return this.getChainId() === Number(this.config.config.chainId);
316
+ }
317
+
318
+ private async switchChain(): Promise<void> {
319
+ if (!this.isOnCorrectChain()) {
320
+ await this.transition(TransactionState.SWITCH_CHAIN);
321
+ await this.switchChainFn(this.config.config.chainId);
322
+ await this.walletClient.switchChain({
323
+ id: Number(this.config.config.chainId),
324
+ });
325
+ debug('Switched chain');
326
+ }
327
+ }
328
+
329
+ async start({ props }: { props: TransactionInput['props'] }) {
330
+ debug('Starting transaction', props);
331
+ try {
332
+ await this.transition(TransactionState.CHECKING_STEPS);
333
+ const { type } = this.config.config;
334
+
335
+ const steps = await this.generateSteps({
336
+ type,
337
+ props,
338
+ } as TransactionInput);
339
+
340
+ for (const step of steps) {
341
+ try {
342
+ await this.executeStep({ step, props });
343
+ } catch (error) {
344
+ await this.transition(TransactionState.ERROR);
345
+ throw error;
346
+ }
347
+ }
348
+
349
+ await this.transition(TransactionState.SUCCESS);
350
+ } catch (error) {
351
+ debug('Transaction failed', error);
352
+ await this.transition(TransactionState.ERROR);
353
+ throw error;
354
+ }
355
+ }
356
+
357
+ private async handleTransactionSuccess(hash?: Hash) {
358
+ if (!hash) {
359
+ // TODO: This is to handle signature steps, but it's not ideal
360
+ await this.transition(TransactionState.SUCCESS);
361
+ return;
362
+ }
363
+ await this.transition(TransactionState.CONFIRMING);
364
+ this.config.onTransactionSent?.(hash);
365
+
366
+ const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
367
+ debug('Transaction confirmed', receipt);
368
+
369
+ await this.transition(TransactionState.SUCCESS);
370
+ this.config.onSuccess?.(hash);
371
+ }
372
+
373
+ private async executeTransaction(step: Step): Promise<Hash> {
374
+ const transactionData = {
375
+ account: this.getAccount(),
376
+ chain: this.getChainForTransaction(),
377
+ to: step.to as Hex,
378
+ data: step.data as Hex,
379
+ value: BigInt(step.value || '0'),
380
+ };
381
+ debug('Executing transaction', transactionData);
382
+ const hash = await this.walletClient.sendTransaction(transactionData);
383
+ debug('Transaction submitted', { hash });
384
+ await this.handleTransactionSuccess(hash);
385
+ return hash;
386
+ }
387
+
388
+ private async executeSignature(step: Step) {
389
+ debug('Executing signature', { stepId: step.id });
390
+ let signature: Hex;
391
+ if (!step.post) {
392
+ throw new Error('Missing post step');
393
+ }
394
+ switch (step.id) {
395
+ case StepType.signEIP712:
396
+ if (!step.signature) {
397
+ throw new Error('Missing signature data');
398
+ }
399
+ signature = await this.walletClient.signTypedData({
400
+ domain: step.signature.domain as TypedDataDomain,
401
+ types: step.signature.types,
402
+ primaryType: step.signature.primaryType,
403
+ account: this.getAccountAddress(),
404
+ message: step.signature.value,
405
+ });
406
+ break;
407
+ case StepType.signEIP191:
408
+ signature = await this.walletClient.signMessage({
409
+ message: step.data,
410
+ account: step.to as Hex,
411
+ });
412
+ break;
413
+ default:
414
+ throw new Error(`Invalid signature step: ${step.id}`);
415
+ }
416
+
417
+ await this.marketplaceClient.execute({
418
+ signature,
419
+ executeType: ExecuteType.order,
420
+ body: step.post,
421
+ });
422
+ await this.handleTransactionSuccess();
423
+ }
424
+
425
+ private openPaymentModalWithPromise(
426
+ settings: Omit<SelectPaymentSettings, 'onSuccess' | 'onError'>,
427
+ ): Promise<void> {
428
+ return new Promise((resolve, reject) => {
429
+ this.openSelectPaymentModal({
430
+ ...settings,
431
+ onSuccess: async (hash: string) => {
432
+ await this.handleTransactionSuccess(hash as Hash);
433
+ resolve();
434
+ },
435
+ onError: (error: Error) => {
436
+ this.config.onError?.(error);
437
+ reject(error);
438
+ },
439
+ });
440
+ });
441
+ }
442
+
443
+ private async executeBuyStep({
444
+ step,
445
+ props,
446
+ }: {
447
+ step: Step;
448
+ props: BuyInput;
449
+ }) {
450
+ this.transition(TransactionState.EXECUTING_TRANSACTION);
451
+ const [checkoutOptions, orders] = await Promise.all([
452
+ this.marketplaceClient.checkoutOptionsMarketplace({
453
+ wallet: this.getAccountAddress(),
454
+ orders: [
455
+ {
456
+ contractAddress: this.config.config.collectionAddress,
457
+ orderId: props.orderId,
458
+ marketplace: props.marketplace,
459
+ },
460
+ ],
461
+ additionalFee: Number(
462
+ this.getMarketplaceFee(this.config.config.collectionAddress).amount,
463
+ ),
464
+ }),
465
+ this.marketplaceClient.getOrders({
466
+ input: [
467
+ {
468
+ orderId: props.orderId,
469
+ marketplace: props.marketplace,
470
+ contractAddress: this.config.config.collectionAddress,
471
+ },
472
+ ],
473
+ }),
474
+ ]);
475
+
476
+ const order = orders.orders[0];
477
+
478
+ await this.openPaymentModalWithPromise({
479
+ // biome-ignore lint/style/noNonNullAssertion: <explanation>
480
+ chain: this.getChainId()!,
481
+ collectibles: [
482
+ {
483
+ tokenId: order.tokenId,
484
+ quantity: props.quantity,
485
+ decimals: props.collectableDecimals,
486
+ },
487
+ ],
488
+ currencyAddress: order.priceCurrencyAddress,
489
+ price: order.priceAmount,
490
+ targetContractAddress: step.to,
491
+ txData: step.data as Hex,
492
+ collectionAddress: this.config.config.collectionAddress,
493
+ recipientAddress: this.getAccountAddress(),
494
+ enableMainCurrencyPayment: true,
495
+ enableSwapPayments: !!checkoutOptions.options?.swap?.includes(
496
+ TransactionSwapProvider.zerox,
497
+ ),
498
+ creditCardProviders: checkoutOptions?.options.nftCheckout || [],
499
+ });
500
+ }
501
+
502
+ private async executeStep({
503
+ step,
504
+ props,
505
+ }: {
506
+ step: Step;
507
+ props: TransactionInput['props'];
508
+ }) {
509
+ debug('Executing step', { stepId: step.id });
510
+ if (!step.to && !step.signature) {
511
+ throw new Error('Invalid step data');
512
+ }
513
+
514
+ try {
515
+ await this.switchChain();
516
+ if (step.id === StepType.buy) {
517
+ await this.executeBuyStep({ step, props: props as BuyInput });
518
+ } else if (step.signature) {
519
+ await this.executeSignature(step);
520
+ } else if (step.id === StepType.tokenApproval) {
521
+ //TODO: Add some sort ofs callback heres
522
+ const hash = await this.executeTransaction(step);
523
+ return { hash }
524
+ } else {
525
+ const hash = await this.executeTransaction(step);
526
+ this.config.onSuccess?.(hash);
527
+ return { hash };
528
+ }
529
+ } catch (error) {
530
+ this.config.onError?.(error as Error);
531
+ throw error;
532
+ }
533
+ }
534
+
535
+ async getTransactionSteps(
536
+ props: TransactionInput['props'],
537
+ ): Promise<TransactionSteps> {
538
+ debug('Getting transaction steps', props);
539
+ // Return memoized value if props and state haven't changed
540
+ if (
541
+ this.memoizedSteps &&
542
+ this.lastProps &&
543
+ JSON.stringify(props) === JSON.stringify(this.lastProps)
544
+ ) {
545
+ debug('Returning memoized steps');
546
+ return this.memoizedSteps;
547
+ }
548
+
549
+ const type = this.config.config.type;
550
+ const steps = await this.generateSteps({
551
+ type,
552
+ props,
553
+ } as TransactionInput);
554
+ // Extract execution step, it should always be the last step
555
+ const executionStep = steps.pop();
556
+ if (!executionStep) {
557
+ throw new Error('No steps found');
558
+ }
559
+ if (executionStep.id === StepType.tokenApproval) {
560
+ throw new Error('No execution step found, only approval step');
561
+ }
562
+ const approvalStep = steps.pop();
563
+
564
+ if (steps.length > 0) {
565
+ throw new Error('Unexpected steps found');
566
+ }
567
+
568
+ this.lastProps = props;
569
+ this.memoizedSteps = {
570
+ switchChain: {
571
+ isPending: !this.isOnCorrectChain(),
572
+ isExecuting: this.currentState === TransactionState.SWITCH_CHAIN,
573
+ execute: () => this.switchChain(),
574
+ },
575
+ approval: {
576
+ isPending: Boolean(approvalStep),
577
+ isExecuting: this.currentState === TransactionState.TOKEN_APPROVAL,
578
+ execute: () =>
579
+ approvalStep && this.executeStep({ step: approvalStep, props }),
580
+ },
581
+ transaction: {
582
+ isPending: Boolean(executionStep),
583
+ isExecuting:
584
+ this.currentState === TransactionState.EXECUTING_TRANSACTION,
585
+ execute: () => this.executeStep({ step: executionStep, props }),
586
+ },
587
+ } as const;
588
+
589
+ debug('Generated new transaction steps', this.memoizedSteps);
590
+ return this.memoizedSteps;
591
+ }
592
+ }
@@ -0,0 +1,66 @@
1
+ import { useSelectPaymentModal } from '@0xsequence/kit-checkout';
2
+ import type { Hash } from 'viem';
3
+ import { useAccount, useSwitchChain, useWalletClient } from 'wagmi';
4
+ import { getPublicRpcClient } from '../../../utils';
5
+ import { useConfig, useMarketplaceConfig } from '../../hooks';
6
+ import { useSwitchChainModal } from '../../ui/modals/_internal/components/switchChainModal';
7
+ import { WalletKind } from '../api';
8
+ import {
9
+ type TransactionConfig,
10
+ TransactionMachine,
11
+ } from './execute-transaction';
12
+
13
+ export type UseTransactionMachineConfig = Omit<
14
+ TransactionConfig,
15
+ 'sdkConfig' | 'marketplaceConfig' | 'walletKind' | 'chains'
16
+ >;
17
+
18
+ export const useTransactionMachine = (
19
+ config: UseTransactionMachineConfig,
20
+ onSuccess?: (hash: Hash) => void,
21
+ onError?: (error: Error) => void,
22
+ onTransactionSent?: (hash: Hash) => void,
23
+ ) => {
24
+ const { data: walletClient } = useWalletClient();
25
+ const { show: showSwitchChainModal } = useSwitchChainModal();
26
+ const sdkConfig = useConfig();
27
+ const { data: marketplaceConfig, error: marketplaceError } =
28
+ useMarketplaceConfig();
29
+ const { openSelectPaymentModal } = useSelectPaymentModal();
30
+ const { chains } = useSwitchChain();
31
+
32
+ const { connector } = useAccount();
33
+ const walletKind =
34
+ connector?.id === 'sequence' ? WalletKind.sequence : WalletKind.unknown;
35
+
36
+ if (marketplaceError) {
37
+ throw marketplaceError; //TODO: Add error handling
38
+ }
39
+
40
+ if (!walletClient || !marketplaceConfig) return null;
41
+
42
+ return new TransactionMachine(
43
+ {
44
+ config: { sdkConfig, marketplaceConfig, walletKind, chains, ...config },
45
+ onSuccess,
46
+ onError,
47
+ onTransactionSent,
48
+ },
49
+ walletClient,
50
+ getPublicRpcClient(config.chainId),
51
+ openSelectPaymentModal,
52
+ async (chainId) => {
53
+ return new Promise<void>((resolve, reject) => {
54
+ showSwitchChainModal({
55
+ chainIdToSwitchTo: Number(chainId),
56
+ onSuccess: () => {
57
+ resolve();
58
+ },
59
+ onError: (error) => {
60
+ reject(error);
61
+ },
62
+ });
63
+ });
64
+ },
65
+ );
66
+ };
@@ -18,6 +18,10 @@ export * from './useRoyaltyPercentage';
18
18
  export * from './useGenerateListingTransaction';
19
19
  export * from './useGenerateOfferTransaction';
20
20
  export * from './useGenerateSellTransaction';
21
+ export * from './useGenerateCancelTransaction';
21
22
  export * from './useTransferTokens';
22
23
  export * from './useCheckoutOptions';
23
24
  export * from './useListCollections';
25
+ export * from './useGenerateBuyTransaction';
26
+ export * from './useCancelOrder';
27
+ export * from './useBuyCollectable';
@@ -0,0 +1,38 @@
1
+ import {
2
+ type BuyInput,
3
+ TransactionType,
4
+ } from '../_internal/transaction-machine/execute-transaction';
5
+ import {
6
+ useTransactionMachine,
7
+ type UseTransactionMachineConfig,
8
+ } from '../_internal/transaction-machine/useTransactionMachine';
9
+
10
+ interface UseBuyOrderArgs extends Omit<UseTransactionMachineConfig, 'type'> {
11
+ onSuccess?: (hash: string) => void;
12
+ onError?: (error: Error) => void;
13
+ onTransactionSent?: (hash: string) => void;
14
+ }
15
+
16
+ export const useBuyCollectable = ({
17
+ onSuccess,
18
+ onError,
19
+ onTransactionSent,
20
+ ...config
21
+ }: UseBuyOrderArgs) => {
22
+ const machine = useTransactionMachine(
23
+ {
24
+ ...config,
25
+ type: TransactionType.BUY,
26
+ },
27
+ onSuccess,
28
+ onError,
29
+ onTransactionSent,
30
+ );
31
+
32
+ return {
33
+ buy: (props: BuyInput) => machine?.start({ props }),
34
+ onError,
35
+ onSuccess,
36
+ onTransactionSent,
37
+ };
38
+ };