@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.
- package/dist/chunk-22NLQ3AS.js +3078 -0
- package/dist/chunk-22NLQ3AS.js.map +1 -0
- package/dist/chunk-3OHM45R3.js +1294 -0
- package/dist/chunk-3OHM45R3.js.map +1 -0
- package/dist/{chunk-MQR6SHXZ.js → chunk-4YU2UPYH.js} +58 -103
- package/dist/chunk-4YU2UPYH.js.map +1 -0
- package/dist/chunk-7NJETFMF.js +21 -0
- package/dist/chunk-7NJETFMF.js.map +1 -0
- package/dist/{chunk-UYRQ5MJQ.js → chunk-FUM4OGOQ.js} +4 -4
- package/dist/chunk-FUM4OGOQ.js.map +1 -0
- package/dist/{chunk-7OO74L2K.js → chunk-GJAKQ5Q3.js} +40 -1
- package/dist/chunk-GJAKQ5Q3.js.map +1 -0
- package/dist/chunk-MCI3KOSQ.js +2 -0
- package/dist/{chunk-BJE7AG6V.js → chunk-O7UQGT43.js} +698 -23
- package/dist/chunk-O7UQGT43.js.map +1 -0
- package/dist/{chunk-CSN7YD5Q.js → chunk-Q57TEA3Z.js} +20 -2
- package/dist/chunk-Q57TEA3Z.js.map +1 -0
- package/dist/{chunk-VEX7FDL6.js → chunk-SBVLWSRZ.js} +2 -2
- package/dist/{chunk-VEX7FDL6.js.map → chunk-SBVLWSRZ.js.map} +1 -1
- package/dist/{chunk-6S4FYXP6.js → chunk-SPW24Y7I.js} +40 -1
- package/dist/chunk-SPW24Y7I.js.map +1 -0
- package/dist/chunk-UISBTKFF.js +1 -0
- package/dist/{chunk-OUWB3FHZ.js → chunk-WA433WAJ.js} +9 -33
- package/dist/chunk-WA433WAJ.js.map +1 -0
- package/dist/{chunk-O5JXKTWP.js → chunk-WFE6OCYF.js} +4 -4
- package/dist/chunk-WFE6OCYF.js.map +1 -0
- package/dist/chunk-XX4EVWBF.js +1292 -0
- package/dist/chunk-XX4EVWBF.js.map +1 -0
- package/dist/chunk-Y7YO5TLE.js +53 -0
- package/dist/chunk-Y7YO5TLE.js.map +1 -0
- package/dist/index.css +1 -50
- package/dist/index.d.ts +3 -5
- package/dist/index.js +12 -14
- package/dist/index.js.map +1 -1
- package/dist/react/hooks/index.css +82 -0
- package/dist/react/hooks/index.css.map +1 -0
- package/dist/react/hooks/index.d.ts +401 -462
- package/dist/react/hooks/index.js +26 -6
- package/dist/react/index.css +56 -91
- package/dist/react/index.css.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +32 -13
- package/dist/react/ui/components/index.css +86 -121
- package/dist/react/ui/components/index.css.map +1 -1
- package/dist/react/ui/components/index.d.ts +10 -4
- package/dist/react/ui/components/index.js +12 -11
- package/dist/react/ui/icons/index.js +3 -2
- package/dist/react/ui/icons/index.js.map +1 -1
- package/dist/react/ui/index.css +56 -91
- package/dist/react/ui/index.css.map +1 -1
- package/dist/react/ui/index.d.ts +29 -31
- package/dist/react/ui/index.js +14 -11
- package/dist/react/ui/modals/_internal/components/actionModal/index.js +5 -16
- package/dist/react/ui/modals/_internal/components/actionModal/index.js.map +1 -1
- package/dist/styles/index.css +1 -50
- package/dist/styles/index.css.map +1 -1
- package/dist/styles/index.d.ts +2 -6
- package/dist/styles/index.js +9 -11
- package/dist/utils/abi/index.d.ts +2 -0
- package/dist/utils/abi/index.js +21 -0
- package/dist/utils/abi/marketplace/index.d.ts +805 -0
- package/dist/utils/abi/marketplace/index.js +12 -0
- package/dist/utils/abi/{abi/token → token}/index.js +1 -2
- package/dist/utils/index.d.ts +5 -5
- package/dist/utils/index.js +12 -14
- package/package.json +17 -15
- package/src/react/_internal/transaction-machine/execute-transaction.ts +592 -0
- package/src/react/_internal/transaction-machine/useTransactionMachine.ts +66 -0
- package/src/react/hooks/index.ts +4 -0
- package/src/react/hooks/useBuyCollectable.tsx +38 -0
- package/src/react/hooks/useCancelOrder.tsx +38 -0
- package/src/react/hooks/useCheckoutOptions.tsx +9 -6
- package/src/react/hooks/useCreateListing.tsx +65 -0
- package/src/react/hooks/useGenerateBuyTransaction.tsx +71 -0
- package/src/react/hooks/useListListingsForCollectible.tsx +1 -1
- package/src/react/hooks/useMakeOffer.tsx +62 -0
- package/src/react/hooks/useRoyaltyPercentage.tsx +1 -1
- package/src/react/hooks/useSell.tsx +62 -0
- package/src/react/ui/components/_internals/action-button/ActionButton.tsx +107 -115
- package/src/react/ui/components/_internals/custom-select/CustomSelect.tsx +63 -0
- package/src/react/ui/components/_internals/custom-select/styles.css.ts +64 -0
- package/src/react/ui/components/collectible-card/CollectibleCard.tsx +127 -130
- package/src/react/ui/components/collectible-card/Footer.tsx +65 -58
- package/src/react/ui/icons/Bell.tsx +2 -2
- package/src/react/ui/index.ts +1 -0
- package/src/react/ui/modals/BuyModal/_store.ts +53 -0
- package/src/react/ui/modals/BuyModal/index.tsx +119 -0
- package/src/react/ui/modals/CreateListingModal/_store.ts +35 -312
- package/src/react/ui/modals/CreateListingModal/index.tsx +185 -126
- package/src/react/ui/modals/MakeOfferModal/_store.ts +34 -276
- package/src/react/ui/modals/MakeOfferModal/index.tsx +195 -136
- package/src/react/ui/modals/SellModal/_store.ts +29 -262
- package/src/react/ui/modals/SellModal/index.tsx +156 -121
- package/src/react/ui/modals/SuccessfulPurchaseModal/_store.ts +17 -3
- package/src/react/ui/modals/SuccessfulPurchaseModal/index.tsx +3 -2
- package/src/react/ui/modals/TransferModal/index.tsx +9 -16
- package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +1 -0
- package/src/react/ui/modals/_internal/components/actionModal/ErrorModal.tsx +18 -0
- package/src/react/ui/modals/_internal/components/actionModal/LoadingModal.tsx +18 -0
- package/src/react/ui/modals/_internal/components/calendarPopover/index.tsx +1 -0
- package/src/react/ui/modals/_internal/components/calendarPopover/overrides.css +8 -0
- package/src/react/ui/modals/_internal/components/calendarPopover/styles.css.ts +10 -4
- package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +10 -13
- package/src/react/ui/modals/_internal/components/expirationDateSelect/index.tsx +23 -9
- package/src/react/ui/modals/_internal/components/priceInput/index.tsx +7 -18
- package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +17 -44
- package/src/react/ui/modals/_internal/components/switchChainModal/store.ts +10 -8
- package/src/react/ui/modals/_internal/components/tokenPreview/index.tsx +14 -3
- package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +4 -2
- package/src/react/ui/modals/_internal/components/transactionHeader/index.tsx +4 -4
- package/src/react/ui/modals/_internal/components/transactionPreview/index.tsx +4 -2
- package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +13 -10
- package/src/react/ui/modals/_internal/types.ts +13 -0
- package/src/react/ui/modals/modal-provider.tsx +4 -2
- package/src/styles/index.ts +0 -2
- package/src/utils/abi/index.ts +2 -0
- package/src/utils/abi/marketplace/index.ts +3 -0
- package/src/utils/abi/marketplace/sequence-marketplace-v1.ts +463 -0
- package/src/utils/abi/marketplace/sequence-marketplace-v2.ts +802 -0
- package/src/utils/index.ts +2 -3
- package/src/utils/network.ts +4 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/chunk-6JWGELXL.js +0 -214
- package/dist/chunk-6JWGELXL.js.map +0 -1
- package/dist/chunk-6S4FYXP6.js.map +0 -1
- package/dist/chunk-7OO74L2K.js.map +0 -1
- package/dist/chunk-BCNFYVAL.js +0 -1
- package/dist/chunk-BJE7AG6V.js.map +0 -1
- package/dist/chunk-CSN7YD5Q.js.map +0 -1
- package/dist/chunk-D7QQP6MS.js +0 -2
- package/dist/chunk-DBFOPEV6.js +0 -23
- package/dist/chunk-DBFOPEV6.js.map +0 -1
- package/dist/chunk-MQR6SHXZ.js.map +0 -1
- package/dist/chunk-O5JXKTWP.js.map +0 -1
- package/dist/chunk-OUWB3FHZ.js.map +0 -1
- package/dist/chunk-PE2LLUTJ.js +0 -213
- package/dist/chunk-PE2LLUTJ.js.map +0 -1
- package/dist/chunk-QVFMD6S2.js +0 -21
- package/dist/chunk-QVFMD6S2.js.map +0 -1
- package/dist/chunk-QXLZPSSR.js +0 -3316
- package/dist/chunk-QXLZPSSR.js.map +0 -1
- package/dist/chunk-UYRQ5MJQ.js.map +0 -1
- package/dist/utils/abi/abi/standard/index.d.ts +0 -25
- package/dist/utils/abi/abi/standard/index.js +0 -8
- package/dist/utils/abi/clients/index.d.ts +0 -27
- package/dist/utils/abi/clients/index.js +0 -13
- package/src/react/ui/modals/_internal/components/currencyOptionsSelect/styles.css.ts +0 -33
- package/src/react/ui/modals/_internal/components/expirationDateSelect/styles.css.ts +0 -25
- package/src/utils/abi/abi/standard/index.ts +0 -1
- package/src/utils/abi/clients/ERC1155.ts +0 -82
- package/src/utils/abi/clients/ERC20.ts +0 -101
- package/src/utils/abi/clients/ERC721.ts +0 -97
- package/src/utils/abi/clients/index.ts +0 -3
- /package/dist/{chunk-BCNFYVAL.js.map → chunk-MCI3KOSQ.js.map} +0 -0
- /package/dist/{chunk-D7QQP6MS.js.map → chunk-UISBTKFF.js.map} +0 -0
- /package/dist/utils/abi/{abi/standard/index.js.map → index.js.map} +0 -0
- /package/dist/utils/abi/{abi/token → marketplace}/index.js.map +0 -0
- /package/dist/utils/abi/{abi/token → token}/index.d.ts +0 -0
- /package/dist/utils/abi/{clients → token}/index.js.map +0 -0
- /package/src/react/hooks/{useGenerateCancleTransaction.tsx → useGenerateCancelTransaction.tsx} +0 -0
- /package/src/utils/abi/{abi/standard → marketplace}/EIP2981.ts +0 -0
- /package/src/utils/abi/{abi/token → token}/ERC1155.ts +0 -0
- /package/src/utils/abi/{abi/token → token}/ERC20.ts +0 -0
- /package/src/utils/abi/{abi/token → token}/ERC721.ts +0 -0
- /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
|
+
};
|
package/src/react/hooks/index.ts
CHANGED
|
@@ -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
|
+
};
|