@tradeport/sui-trading-sdk 0.5.3 → 0.5.4

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 (153) hide show
  1. package/.babel.config.js +3 -0
  2. package/.claude/settings.json +5 -0
  3. package/.claude/settings.local.json +5 -0
  4. package/.env.demo +5 -0
  5. package/.eslintrc.json +46 -0
  6. package/.prettierignore +4 -0
  7. package/.prettierrc.json +7 -0
  8. package/.vscode/launch.json +23 -0
  9. package/CHANGELOG.md +746 -0
  10. package/jest.config.js +12 -0
  11. package/package.json +66 -71
  12. package/src/SuiTradingClient.ts +442 -0
  13. package/src/apiClients/createKioskClient.ts +10 -0
  14. package/src/apiClients/createSuiClient.ts +5 -0
  15. package/src/apiClients/graphqlClient.ts +17 -0
  16. package/src/bigNumberConfig.ts +10 -0
  17. package/src/constants.ts +1052 -0
  18. package/src/graphql/createChainGQLQuery.ts +27 -0
  19. package/src/graphql/gqlChainRequest.ts +21 -0
  20. package/src/graphql/queries/fetchAccountKiosks.ts +12 -0
  21. package/src/graphql/queries/fetchActiveLockStateByNftId.ts +15 -0
  22. package/src/graphql/queries/fetchBidsById.ts +36 -0
  23. package/src/graphql/queries/fetchCollectionBidById.ts +21 -0
  24. package/src/graphql/queries/fetchCollectionBidsAtSamePrice.ts +29 -0
  25. package/src/graphql/queries/fetchCollectionChainState.ts +17 -0
  26. package/src/graphql/queries/fetchCollectionFloorListingForMarket.ts +28 -0
  27. package/src/graphql/queries/fetchCollectionFloorListings.ts +15 -0
  28. package/src/graphql/queries/fetchCollectionsById.ts +39 -0
  29. package/src/graphql/queries/fetchCommissionByListingId.ts +13 -0
  30. package/src/graphql/queries/fetchCryptoToUsdRate.ts +13 -0
  31. package/src/graphql/queries/fetchKiosksByOwner.ts +14 -0
  32. package/src/graphql/queries/fetchListingsById.ts +29 -0
  33. package/src/graphql/queries/fetchListingsByNftId.ts +23 -0
  34. package/src/graphql/queries/fetchLockById.ts +23 -0
  35. package/src/graphql/queries/fetchMultibidById.ts +34 -0
  36. package/src/graphql/queries/fetchNftCollectionChainState.ts +12 -0
  37. package/src/graphql/queries/fetchNftsById.ts +75 -0
  38. package/src/graphql/queries/fetchNftsByKioskId.ts +45 -0
  39. package/src/graphql/queries/fetchOwnerCapByKiosk.ts +10 -0
  40. package/src/graphql/queries/fetchPersonalCapByKiosk.ts +10 -0
  41. package/src/graphql/queries/fetchSharedObjectsByType.ts +12 -0
  42. package/src/graphql/queries/fetchTransferPoliciesByType.ts +12 -0
  43. package/src/graphql/queries/fetchWalletKiosks.ts +14 -0
  44. package/src/graphql/queries/getCommissionByListingId.ts +15 -0
  45. package/src/helpers/addOneDollarFee.ts +17 -0
  46. package/src/helpers/addThirdPartyTxFee.ts +17 -0
  47. package/src/helpers/calculatePremium.ts +9 -0
  48. package/src/helpers/calculateRoyaltyFee.ts +28 -0
  49. package/src/helpers/destroyZeroCoin.ts +14 -0
  50. package/src/helpers/getActiveLockStateByNftId.ts +15 -0
  51. package/src/helpers/getCollectionChainState.ts +31 -0
  52. package/src/helpers/getCollectionFloorPrice.ts +12 -0
  53. package/src/helpers/getLockById.ts +29 -0
  54. package/src/helpers/getMarketFeePrice.ts +82 -0
  55. package/src/helpers/getNftType.ts +38 -0
  56. package/src/helpers/getRoyaltyRuleModule.ts +7 -0
  57. package/src/helpers/getSharedObjects.ts +119 -0
  58. package/src/helpers/getSuiToUsdRate.ts +14 -0
  59. package/src/helpers/getTradeportBiddingContractBidAmount.ts +31 -0
  60. package/src/helpers/getTransferPolicyRuleNamesFromSuiObject.ts +24 -0
  61. package/src/helpers/hasPersonalKioskRule.ts +7 -0
  62. package/src/helpers/hasRoyaltyRule.ts +7 -0
  63. package/src/helpers/hasTransferPolicyRules.ts +6 -0
  64. package/src/helpers/isExpiredListing.ts +9 -0
  65. package/src/helpers/isNonKioskListing.ts +14 -0
  66. package/src/helpers/isSIngleBid.ts +13 -0
  67. package/src/helpers/kiosk/assertNftInSharedKiosk.ts +31 -0
  68. package/src/helpers/kiosk/getKioskCollectionRoyaltyFeeAmount.ts +71 -0
  69. package/src/helpers/kiosk/getKioskIdFromKioskTx.ts +29 -0
  70. package/src/helpers/kiosk/getKioskPlaceBidCoin.ts +59 -0
  71. package/src/helpers/kiosk/getNativeKioskTransferPolicies.ts +29 -0
  72. package/src/helpers/kiosk/getNativeKioskTransferPoliciesByNftType.ts +21 -0
  73. package/src/helpers/kiosk/getRulePackageId.ts +59 -0
  74. package/src/helpers/kiosk/getTransferPoliciesToResolve.ts +154 -0
  75. package/src/helpers/kiosk/isBluemoveKioskBid.ts +1 -0
  76. package/src/helpers/kiosk/isTradePortKioskBid.ts +4 -0
  77. package/src/helpers/kiosk/kioskListingBcs.ts +19 -0
  78. package/src/helpers/kiosk/kioskTxWrapper.ts +152 -0
  79. package/src/helpers/kiosk/lockNftInsideKioskIfNotLocked.ts +100 -0
  80. package/src/helpers/kiosk/preProcessSharedBulkBuyingData.ts +171 -0
  81. package/src/helpers/kiosk/resolveCustomTransferPolicyRules.ts +75 -0
  82. package/src/helpers/kiosk/resolveFloorPriceRule.ts +21 -0
  83. package/src/helpers/kiosk/resolveKioskLockRule.ts +47 -0
  84. package/src/helpers/kiosk/resolvePersonalKioskRule.ts +23 -0
  85. package/src/helpers/kiosk/resolveRoyaltyRule.ts +57 -0
  86. package/src/helpers/kiosk/resolveTableAllowlistRule.ts +34 -0
  87. package/src/helpers/kiosk/resolveTransferPolicies.ts +158 -0
  88. package/src/helpers/kiosk/sharedKioskState.type.ts +11 -0
  89. package/src/helpers/kiosk/takeAndBorrowNftFromKiosk.ts +50 -0
  90. package/src/helpers/kiosk/takeLockedNftFromKiosk.ts +73 -0
  91. package/src/helpers/kiosk/upgradeKioskRulesPackageId.ts +17 -0
  92. package/src/helpers/originByte/confirmOBTranfer.ts +33 -0
  93. package/src/helpers/originByte/createOBKiosk.ts +14 -0
  94. package/src/helpers/originByte/depositNftIntoOBKiosk.ts +16 -0
  95. package/src/helpers/originByte/getOBBidderKiosk.ts +12 -0
  96. package/src/helpers/originByte/getOBKiosk.ts +24 -0
  97. package/src/helpers/originByte/getOrCreateOBKiosk.ts +42 -0
  98. package/src/helpers/originByte/isOBKiosk.ts +13 -0
  99. package/src/helpers/originByte/isOriginByteBid.ts +30 -0
  100. package/src/helpers/originByte/isOriginByteCollection.ts +11 -0
  101. package/src/helpers/originByte/shareOriginByteKiosk.ts +14 -0
  102. package/src/helpers/rpc/getAcountBalance.ts +18 -0
  103. package/src/helpers/rpc/getObjectType.ts +25 -0
  104. package/src/helpers/splitCoins.ts +12 -0
  105. package/src/helpers/track.ts +20 -0
  106. package/src/helpers/validateMinFloorPrice.ts +22 -0
  107. package/src/index.ts +2 -0
  108. package/src/methods/acceptCollectionBid/acceptCollectionBid.ts +189 -0
  109. package/src/methods/acceptCollectionBid/addAcceptCollectionBIdTxs.ts +398 -0
  110. package/src/methods/acceptNftBids/acceptNftBids.ts +174 -0
  111. package/src/methods/acceptNftBids/addAcceptNftBidTxs.ts +438 -0
  112. package/src/methods/applyFtStrategy/applyFtStrategy.ts +55 -0
  113. package/src/methods/applyNftStrategy/applyNftStrategy.ts +90 -0
  114. package/src/methods/buyListings/addBuyListingTxs.ts +806 -0
  115. package/src/methods/buyListings/buyListings.ts +230 -0
  116. package/src/methods/cancelMultiBid/cancelMultiBid.ts +51 -0
  117. package/src/methods/cancelNftTransfers/addCancelNftTransfersTx.ts +20 -0
  118. package/src/methods/cancelNftTransfers/cancelNftTransfers.ts +118 -0
  119. package/src/methods/claimNfts/addClaimNftsTxs.ts +247 -0
  120. package/src/methods/claimNfts/claimNfts.ts +270 -0
  121. package/src/methods/createMultiBid/createMultiBid.ts +36 -0
  122. package/src/methods/listNfts/addListTxs.ts +210 -0
  123. package/src/methods/listNfts/addRelistTxs.ts +87 -0
  124. package/src/methods/listNfts/listNfts.ts +152 -0
  125. package/src/methods/migrateNftsFromUnsharedToSharedKiosks/migrateNftsFromUnsharedToSharedKiosks.ts +226 -0
  126. package/src/methods/placeCollectionBids/addPlaceCollectionBidTxs.ts +149 -0
  127. package/src/methods/placeCollectionBids/placeCollectionBids.ts +153 -0
  128. package/src/methods/placeNftBids/addPlaceNftBidTxs.ts +154 -0
  129. package/src/methods/placeNftBids/placeNftBids.ts +111 -0
  130. package/src/methods/relistNft/relistNft.ts +153 -0
  131. package/src/methods/removeCollectionBids/addRemoveCollectionBidsTxs.ts +132 -0
  132. package/src/methods/removeCollectionBids/removeCollectionBids.ts +117 -0
  133. package/src/methods/removeNftBids/addRemoveNftBidTxs.ts +122 -0
  134. package/src/methods/removeNftBids/removeNftBids.ts +116 -0
  135. package/src/methods/sponsorNftListing/addSponsorNftListingTx.ts +40 -0
  136. package/src/methods/sponsorNftListing/sponsorNftListing.ts +60 -0
  137. package/src/methods/transferNfts/addTransferNftTx.ts +220 -0
  138. package/src/methods/transferNfts/transferNfts.ts +216 -0
  139. package/src/methods/unlistListings/addUnlistListingTxs.ts +399 -0
  140. package/src/methods/unlistListings/unlistListings.ts +158 -0
  141. package/src/methods/updateMultiBid/updateMultiBid.ts +66 -0
  142. package/src/methods/withdrawProfitsFromKiosks/withdrawProfitsFromKiosks.ts +100 -0
  143. package/src/tests/SuiWallet.ts +83 -0
  144. package/src/utils/addHexPrefix.ts +7 -0
  145. package/src/utils/addLeadingZerosAfter0x.ts +9 -0
  146. package/src/utils/normalizeNftType.ts +7 -0
  147. package/src/utils/printTxBlockTxs.ts +11 -0
  148. package/src/utils/pureValues.ts +13 -0
  149. package/src/utils/toUint8Array.ts +12 -0
  150. package/tsconfig.json +16 -0
  151. package/dist/index.d.ts +0 -194
  152. package/dist/index.js +0 -7419
  153. package/dist/index.js.map +0 -1
@@ -0,0 +1,806 @@
1
+ import { bcs } from '@mysten/sui/bcs';
2
+ import { KioskListing } from '../../helpers/kiosk/kioskListingBcs';
3
+ import {
4
+ BLUEMOVE_CREATOR_CONFIG_OBJECT,
5
+ BLUEMOVE_KIOSK_MARKETPLACE_KIOSK_OBJECT,
6
+ BLUEMOVE_MARKET_CONFIG_OBJECT,
7
+ BLUEMOVE_ROYALTY_COLLECTION_OBJECT,
8
+ HYPERSPACE_MP_TRANSFER_POLICY_TYPE,
9
+ HYPERSPACE_TRANSFER_POLICY_TYPE,
10
+ KEEPSAKE_MARKETPLACE_KIOSK,
11
+ KEEPSAKE_MARKETPLACE_OBJECT,
12
+ NON_KIOSK_LISTING_NONCE_TYPE,
13
+ SOUFFL3_EXTENSION_OBJECT,
14
+ SOUFFL3_GENERIC_BUY_METHOD_COLLECTIONS,
15
+ SOUFFL3_MARKETPLACE_OBJECT,
16
+ SOUFFL3_VERSION_OBJECT,
17
+ TOCEN_MARKETPLACE_OBJECT,
18
+ TRADEPORT_LISTINGS_DEFAULT_COMMISSION_BPS,
19
+ TRADEPORT_KIOSK_LISTING_STORE,
20
+ TRADEPORT_LISTING_STORE,
21
+ TRADEPORT_LISTINGS_PACKAGE,
22
+ CUSTOM_TRANSFER_RULES_BY_COLLECTION,
23
+ TRADEPORT_LISTINGS_STORE,
24
+ XOCIETY_FRONTIER_COLLECTION_ID,
25
+ TRADEPORT_ORDERBOOK_STORE,
26
+ } from '../../constants';
27
+ import { getCommissionByNftContractId } from '../../graphql/queries/getCommissionByListingId';
28
+ import { destroyZeroCoin } from '../../helpers/destroyZeroCoin';
29
+ import { getMarketFeePrice } from '../../helpers/getMarketFeePrice';
30
+ import { getSharedObjects } from '../../helpers/getSharedObjects';
31
+ import { assertNftInSharedKiosk } from '../../helpers/kiosk/assertNftInSharedKiosk';
32
+ import { getNativeKioskTransferPolicies } from '../../helpers/kiosk/getNativeKioskTransferPolicies';
33
+ import { kioskTxWrapper } from '../../helpers/kiosk/kioskTxWrapper';
34
+ import { resolveCustomTransferPolicyRules } from '../../helpers/kiosk/resolveCustomTransferPolicyRules';
35
+ import { resolveTransferPolicies } from '../../helpers/kiosk/resolveTransferPolicies';
36
+ import { confirmOBTranfer } from '../../helpers/originByte/confirmOBTranfer';
37
+ import { getOrCreateOBKiosk } from '../../helpers/originByte/getOrCreateOBKiosk';
38
+ import { isOriginByteCollection } from '../../helpers/originByte/isOriginByteCollection';
39
+ import { shareOriginByteKiosk } from '../../helpers/originByte/shareOriginByteKiosk';
40
+ import { addLeadingZerosAfter0x } from '../../utils/addLeadingZerosAfter0x';
41
+ import { type BuyTx, type TocenBuyTx } from './buyListings';
42
+ import BigNumber from '../../bigNumberConfig';
43
+ import { calculateRoyaltyFee } from '../../helpers/calculateRoyaltyFee';
44
+ import { getCollectionChainState } from '../../helpers/getCollectionChainState';
45
+ import { normalizeSuiObjectId } from '@mysten/sui/utils';
46
+ import { applyFtStrategy } from '../applyFtStrategy/applyFtStrategy';
47
+
48
+ export function addLegacyTradePortBuyTx({
49
+ tx,
50
+ nftType,
51
+ listingNonce,
52
+ price,
53
+ sharedObjects,
54
+ coinToSplit,
55
+ }: BuyTx) {
56
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
57
+
58
+ if (!coin) throw new Error('Coin could not be split');
59
+
60
+ const { collection, royaltyStrategy } = sharedObjects;
61
+ if (collection && royaltyStrategy) {
62
+ tx.moveCall({
63
+ target:
64
+ '0xb42dbb7413b79394e1a0175af6ae22b69a5c7cc5df259cd78072b6818217c027::listings::ob_buy',
65
+ arguments: [
66
+ tx.object(TRADEPORT_LISTING_STORE),
67
+ tx.pure.address(listingNonce),
68
+ tx.object(coin),
69
+ tx.object(collection),
70
+ tx.object(royaltyStrategy),
71
+ ],
72
+ typeArguments: [nftType],
73
+ });
74
+ } else {
75
+ tx.moveCall({
76
+ target: '0xb42dbb7413b79394e1a0175af6ae22b69a5c7cc5df259cd78072b6818217c027::listings::buy',
77
+ arguments: [
78
+ tx.object(TRADEPORT_LISTING_STORE),
79
+ tx.pure.address(listingNonce),
80
+ tx.object(coin),
81
+ ],
82
+ typeArguments: [nftType],
83
+ });
84
+ }
85
+
86
+ destroyZeroCoin({ tx, coin });
87
+ }
88
+
89
+ export const addLegacyTradeportKioskBuyTx = async ({
90
+ tx,
91
+ transferPolicies,
92
+ kioskTx,
93
+ suiClient,
94
+ kioskClient,
95
+ listingId,
96
+ nftType,
97
+ nftTokenId,
98
+ price,
99
+ sellerKiosk,
100
+ seller,
101
+ coinToSplit,
102
+ beforeResolveKioskTransferRequest,
103
+ sharedBulkBuyingDataByNftType,
104
+ collectionId,
105
+ }: BuyTx) => {
106
+ let commissionFeeAmount: number =
107
+ sharedBulkBuyingDataByNftType?.[nftType]?.commissionFeeAmountsByListingId?.[listingId] ?? 0;
108
+
109
+ if (!commissionFeeAmount) {
110
+ try {
111
+ const { object } = await suiClient.core.getDynamicObjectField({
112
+ parentId: TRADEPORT_KIOSK_LISTING_STORE,
113
+ name: { type: '0x2::object::ID', bcs: bcs.Address.serialize(nftTokenId).toBytes() },
114
+ include: { content: true },
115
+ });
116
+ commissionFeeAmount = Number(KioskListing.parse(object.content).commission);
117
+ } catch {
118
+ throw new Error(`Not found kiosk listing object of token ${nftTokenId}`);
119
+ }
120
+ }
121
+
122
+ if (commissionFeeAmount === 0) {
123
+ // Fallback to default commission if had trouble fetching commission from dynamic field object
124
+ commissionFeeAmount = getMarketFeePrice({ price, collectionId });
125
+ }
126
+
127
+ const coin = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [
128
+ tx.pure.u64(price + commissionFeeAmount),
129
+ ]);
130
+
131
+ if (!coin) throw new Error('Coin could not be split');
132
+
133
+ const [kioskItem, transferRequest] = tx.moveCall({
134
+ target:
135
+ '0x475e98e9c436ec118909f3b9e241d86bcbbc2cf9fba05e99a934823fefb375b7::kiosk_listings::buy',
136
+ arguments: [
137
+ tx.object(TRADEPORT_KIOSK_LISTING_STORE),
138
+ tx.object(sellerKiosk),
139
+ tx.object(kioskTx.kiosk),
140
+ tx.pure.address(nftTokenId),
141
+ tx.object(coin),
142
+ ],
143
+ typeArguments: [nftType],
144
+ });
145
+
146
+ await resolveTransferPolicies({
147
+ tx,
148
+ transferPolicies,
149
+ suiClient,
150
+ walletAddress: seller,
151
+ kioskTx,
152
+ kioskClient,
153
+ nftType,
154
+ price: price?.toString(),
155
+ kioskItem,
156
+ transferRequest,
157
+ coinToSplit,
158
+ beforeResolveKioskTransferRequest,
159
+ transferPoliciesToResolve: sharedBulkBuyingDataByNftType?.[nftType]?.transferPoliciesToResolve,
160
+ royaltyFeeAmount:
161
+ sharedBulkBuyingDataByNftType?.[nftType]?.royaltyFeeAmountsByListingId?.[listingId],
162
+ });
163
+
164
+ destroyZeroCoin({ tx, coin });
165
+ };
166
+
167
+ export async function addTradePortBuyTx({ tx, nftType, nftTokenId, price, coinToSplit }: BuyTx) {
168
+ const marketFee = new BigNumber(price)
169
+ .times(TRADEPORT_LISTINGS_DEFAULT_COMMISSION_BPS)
170
+ .div(10_000)
171
+ .integerValue(BigNumber.ROUND_DOWN)
172
+ .toNumber();
173
+ const totalAmount = new BigNumber(price).plus(marketFee).toNumber();
174
+
175
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(totalAmount)]);
176
+
177
+ if (!coin) throw new Error('Coin could not be split');
178
+
179
+ tx.moveCall({
180
+ target: `${TRADEPORT_LISTINGS_PACKAGE}::tradeport_listings::buy_ob_listing_without_transfer_policy`,
181
+ arguments: [
182
+ tx.object(TRADEPORT_LISTINGS_STORE),
183
+ tx.object(TRADEPORT_ORDERBOOK_STORE),
184
+ tx.pure.id(nftTokenId),
185
+ tx.object(coin),
186
+ ],
187
+ typeArguments: [nftType],
188
+ });
189
+
190
+ destroyZeroCoin({ tx, coin });
191
+ }
192
+
193
+ export async function addKioskTradePortBuyTx({
194
+ tx,
195
+ transferPolicies,
196
+ kioskTx,
197
+ nftType,
198
+ nftTokenId,
199
+ price,
200
+ sellerKiosk,
201
+ coinToSplit,
202
+ buyer,
203
+ collectionId,
204
+ }: BuyTx) {
205
+ const transferPolicyId = getNativeKioskTransferPolicies(transferPolicies)?.at(0)?.id;
206
+
207
+ const marketFee = new BigNumber(price)
208
+ .times(TRADEPORT_LISTINGS_DEFAULT_COMMISSION_BPS)
209
+ .div(10000)
210
+ .integerValue(BigNumber.ROUND_DOWN);
211
+
212
+ const royaltyFee = calculateRoyaltyFee(
213
+ getNativeKioskTransferPolicies(transferPolicies)?.at(0)?.rules,
214
+ new BigNumber(price),
215
+ );
216
+
217
+ const totalAmount = new BigNumber(price).plus(marketFee).plus(royaltyFee).toNumber();
218
+
219
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(totalAmount)]);
220
+
221
+ if (!coin) throw new Error('Coin could not be split');
222
+
223
+ const transferRequest = tx.moveCall({
224
+ target: `${TRADEPORT_LISTINGS_PACKAGE}::tradeport_listings::buy_ob_listing_with_transfer_policy`,
225
+ arguments: [
226
+ tx.object(TRADEPORT_LISTINGS_STORE),
227
+ tx.object(TRADEPORT_ORDERBOOK_STORE),
228
+ tx.object(sellerKiosk),
229
+ tx.object(kioskTx.kiosk),
230
+ tx.object(kioskTx.kioskCap),
231
+ tx.pure.address(buyer),
232
+ tx.pure.id(nftTokenId),
233
+ tx.object(coin),
234
+ tx.object(transferPolicyId),
235
+ ],
236
+ typeArguments: [nftType],
237
+ });
238
+
239
+ // Resolve custom transfer policy rules if this collection has any
240
+ const customRuleConfigs = CUSTOM_TRANSFER_RULES_BY_COLLECTION[collectionId];
241
+ if (customRuleConfigs?.length > 0) {
242
+ resolveCustomTransferPolicyRules({
243
+ tx,
244
+ rules: customRuleConfigs.map((config) => ({
245
+ rule: `${config.packageId}::${config.moduleName}`,
246
+ rulePackageId: config.packageId,
247
+ moduleName: config.moduleName,
248
+ params: config.params,
249
+ })),
250
+ transferRequest,
251
+ nftType,
252
+ policyId: transferPolicyId,
253
+ });
254
+ }
255
+
256
+ tx.moveCall({
257
+ target: '0x2::transfer_policy::confirm_request',
258
+ arguments: [tx.object(transferPolicyId), transferRequest],
259
+ typeArguments: [nftType],
260
+ });
261
+
262
+ destroyZeroCoin({ tx, coin });
263
+ }
264
+
265
+ export async function addOriginByteBuyTx({
266
+ tx,
267
+ sharedObjects,
268
+ buyer,
269
+ sellerKiosk,
270
+ seller,
271
+ nftTokenId,
272
+ nftType,
273
+ listingNonce,
274
+ price,
275
+ sharedKioskState,
276
+ coinToSplit,
277
+ }: BuyTx) {
278
+ await assertNftInSharedKiosk({
279
+ kioskId: sellerKiosk,
280
+ walletAddress: seller,
281
+ sharedKioskState,
282
+ });
283
+
284
+ const { orderbook } = sharedObjects;
285
+ const { kiosk: buyerKiosk, isNewKiosk: isNewBuyerKiosk } = await getOrCreateOBKiosk({
286
+ tx,
287
+ address: buyer,
288
+ sharedKioskState,
289
+ });
290
+
291
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
292
+
293
+ if (!coin) throw new Error('Coin could not be split');
294
+
295
+ const transferRequest = tx.moveCall({
296
+ target:
297
+ '0x8534e4cdfd28709c94330a9783c3d5fe6f5daba0bffb69102ce303c5b38aed5a::orderbook::buy_nft',
298
+ arguments: [
299
+ tx.object(orderbook),
300
+ tx.object(listingNonce),
301
+ tx.object(buyerKiosk),
302
+ tx.pure.address(nftTokenId),
303
+ tx.pure.u64(price),
304
+ tx.object(coin),
305
+ ],
306
+ typeArguments: [nftType, '0x2::sui::SUI'],
307
+ });
308
+
309
+ confirmOBTranfer({ tx, sharedObjects, transferRequest, nftType });
310
+
311
+ destroyZeroCoin({ tx, coin });
312
+
313
+ if (isNewBuyerKiosk) {
314
+ shareOriginByteKiosk({ tx, kiosk: buyerKiosk });
315
+ }
316
+ }
317
+
318
+ export const addHyperspaceKioskBuyTx = async ({
319
+ tx,
320
+ transferPolicies,
321
+ suiClient,
322
+ kioskTx,
323
+ kioskClient,
324
+ nftType,
325
+ nftTokenId,
326
+ price,
327
+ sellerKiosk,
328
+ seller,
329
+ beforeResolveKioskTransferRequest,
330
+ coinToSplit,
331
+ }: BuyTx) => {
332
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
333
+
334
+ if (!coin) throw new Error('Coin could not be split');
335
+
336
+ const [kioskItem, transferRequest, hyperspaceTransferRequest, hyperspaceMpTransferRequest] =
337
+ tx.moveCall({
338
+ target:
339
+ '0x6ea97b03c441edd54ae89224bf9560e583ee66c37e6c246f5db35258e580ba94::hyperspace::purchase',
340
+ arguments: [tx.object(sellerKiosk), tx.pure.address(nftTokenId), tx.object(coin)],
341
+ typeArguments: [nftType, HYPERSPACE_MP_TRANSFER_POLICY_TYPE],
342
+ });
343
+
344
+ const customTransferPolicies = [
345
+ {
346
+ type: HYPERSPACE_TRANSFER_POLICY_TYPE,
347
+ transferRequest: hyperspaceTransferRequest,
348
+ },
349
+ {
350
+ type: HYPERSPACE_MP_TRANSFER_POLICY_TYPE,
351
+ transferRequest: hyperspaceMpTransferRequest,
352
+ },
353
+ ].filter((customTransferPolicy) => customTransferPolicy.transferRequest !== transferRequest);
354
+
355
+ await beforeResolveKioskTransferRequest?.(transferRequest);
356
+
357
+ await resolveTransferPolicies({
358
+ tx,
359
+ transferPolicies,
360
+ suiClient,
361
+ walletAddress: seller,
362
+ kioskTx,
363
+ kioskClient,
364
+ nftType,
365
+ price: price?.toString(),
366
+ kioskItem,
367
+ transferRequest,
368
+ customTransferPolicies,
369
+ });
370
+ };
371
+
372
+ export function addBluemoveBuyTx({ tx, nftTokenId, nftType, price, coinToSplit }: BuyTx) {
373
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
374
+
375
+ if (!coin) throw new Error('Coin could not be split');
376
+
377
+ tx.moveCall({
378
+ target:
379
+ '0xd5dd28cc24009752905689b2ba2bf90bfc8de4549b9123f93519bb8ba9bf9981::marketplace::buy_and_take',
380
+ arguments: [
381
+ tx.object(BLUEMOVE_MARKET_CONFIG_OBJECT),
382
+ tx.object(BLUEMOVE_ROYALTY_COLLECTION_OBJECT),
383
+ tx.object(BLUEMOVE_CREATOR_CONFIG_OBJECT),
384
+ tx.pure.address(nftTokenId),
385
+ tx.object(coin),
386
+ tx.pure.u64(price),
387
+ ],
388
+ typeArguments: [nftType, nftType],
389
+ });
390
+ }
391
+
392
+ export async function addBluemoveKioskBuyTx({
393
+ tx,
394
+ suiClient,
395
+ transferPolicies,
396
+ kioskTx,
397
+ kioskClient,
398
+ nftTokenId,
399
+ nftType,
400
+ price,
401
+ sellerKiosk,
402
+ seller,
403
+ beforeResolveKioskTransferRequest,
404
+ coinToSplit,
405
+ }: BuyTx) {
406
+ const transferPolicyId = getNativeKioskTransferPolicies(transferPolicies)?.at(0)?.id;
407
+
408
+ const [coin1, coin2] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [
409
+ tx.pure.u64(price),
410
+ tx.pure.u64(price * 0.025),
411
+ ]);
412
+
413
+ if (!coin1 || !coin2) throw new Error('Coin could not be split');
414
+
415
+ tx.mergeCoins(tx.object(coin1), [tx.object(coin2)]);
416
+
417
+ const [kioskItem, transferRequest] = tx.moveCall({
418
+ target:
419
+ '0xcc97b74ed95c8e8a3ed88050a898727dee37896da579fc400d482b64db6149ff::kiosk_trade::kiosk_buy_v2',
420
+ arguments: [
421
+ tx.object(BLUEMOVE_KIOSK_MARKETPLACE_KIOSK_OBJECT),
422
+ tx.object(sellerKiosk),
423
+ tx.object(transferPolicyId),
424
+ tx.object(kioskTx.kiosk),
425
+ tx.pure.address(nftTokenId),
426
+ tx.pure.u64(price),
427
+ tx.object(coin1),
428
+ ],
429
+ typeArguments: [nftType],
430
+ });
431
+ await beforeResolveKioskTransferRequest?.(transferRequest);
432
+ await resolveTransferPolicies({
433
+ tx,
434
+ transferPolicies,
435
+ suiClient,
436
+ walletAddress: seller,
437
+ kioskTx,
438
+ kioskClient,
439
+ nftType,
440
+ price: price?.toString(),
441
+ kioskItem,
442
+ transferRequest,
443
+ });
444
+ }
445
+
446
+ export async function addSouffl3BuyTx({
447
+ tx,
448
+ nftType,
449
+ price,
450
+ listingNonce,
451
+ collectionId,
452
+ transferPolicies,
453
+ coinToSplit,
454
+ }: BuyTx) {
455
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
456
+
457
+ const transferPolicyId = getNativeKioskTransferPolicies(transferPolicies)?.at(0)?.id;
458
+
459
+ if (transferPolicyId && !SOUFFL3_GENERIC_BUY_METHOD_COLLECTIONS?.includes(collectionId)) {
460
+ const [coinToDestroy] = tx.moveCall({
461
+ target:
462
+ '0x30d90b5b67be77e6e06f02dae9f0f2fcdad16e38854316ae8a7b4f5b4971f5e0::Market::buy_with_sui_with_ext',
463
+ arguments: [
464
+ tx.object(SOUFFL3_VERSION_OBJECT),
465
+ tx.object(SOUFFL3_EXTENSION_OBJECT),
466
+ tx.object(transferPolicyId),
467
+ tx.object(listingNonce),
468
+ tx.pure.u64(price),
469
+ tx.object(SOUFFL3_MARKETPLACE_OBJECT),
470
+ tx.object(coin),
471
+ ],
472
+ typeArguments: [nftType],
473
+ });
474
+
475
+ destroyZeroCoin({ tx, coin: coinToDestroy });
476
+ } else {
477
+ const [coinToDestroy] = tx.moveCall({
478
+ target:
479
+ '0x30d90b5b67be77e6e06f02dae9f0f2fcdad16e38854316ae8a7b4f5b4971f5e0::Market::buy_generic_with_ext',
480
+ arguments: [
481
+ tx.object(SOUFFL3_VERSION_OBJECT),
482
+ tx.object(SOUFFL3_EXTENSION_OBJECT),
483
+ tx.object(listingNonce),
484
+ tx.pure.u64(price),
485
+ tx.object(SOUFFL3_MARKETPLACE_OBJECT),
486
+ tx.object(coin),
487
+ ],
488
+ typeArguments: [nftType, '0x2::sui::SUI'],
489
+ });
490
+
491
+ destroyZeroCoin({ tx, coin: coinToDestroy });
492
+ }
493
+ }
494
+
495
+ export function addSomisBuyTx({
496
+ tx,
497
+ sharedObjects,
498
+ nftTokenId,
499
+ nftType,
500
+ price,
501
+ coinToSplit,
502
+ }: BuyTx) {
503
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
504
+
505
+ if (!coin) throw new Error('Coin could not be split');
506
+
507
+ tx.moveCall({
508
+ target:
509
+ '0xf0b0beb956e26bde50dbd6ac393026c4525aee3b194a9478f09748f7211b5a02::marketplace::buy_nft',
510
+ arguments: [
511
+ tx.object(sharedObjects?.marketplace),
512
+ tx.pure.address(nftTokenId),
513
+ tx.object(coin),
514
+ ],
515
+ typeArguments: [nftType, '0x2::sui::SUI'],
516
+ });
517
+ }
518
+
519
+ export async function addKeepsakeBuyTx({
520
+ tx,
521
+ sharedObjects,
522
+ buyer,
523
+ nftTokenId,
524
+ nftType,
525
+ price,
526
+ royalty,
527
+ transferPolicies,
528
+ coinToSplit,
529
+ }: BuyTx) {
530
+ const { keepsakeRoyaltyStrategy } = sharedObjects;
531
+ const transferPolicyId = getNativeKioskTransferPolicies(transferPolicies)?.at(0)?.id;
532
+
533
+ const [coin1, coin2] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [
534
+ tx.pure.u64(price),
535
+ tx.pure.u64((price * Number(royalty)) / 100 + price * Number(royalty) * 0.00005),
536
+ ]);
537
+
538
+ if (!coin1 || !coin2) throw new Error('Coin could not be split');
539
+
540
+ const [balance] = tx.moveCall({
541
+ target: '0x2::coin::into_balance',
542
+ arguments: [tx.object(coin2)],
543
+ typeArguments: ['0x2::sui::SUI'],
544
+ });
545
+
546
+ if (!balance) throw new Error('Coin into_balance failed');
547
+
548
+ const [transferRequest] = tx.moveCall({
549
+ target:
550
+ '0x02be8c4a1a3cea4d3255d870d367c87838a8cc2bfe4f216a6b67b153027087a7::keepsake_marketplace::buy',
551
+ arguments: [
552
+ tx.object(KEEPSAKE_MARKETPLACE_OBJECT),
553
+ tx.object(KEEPSAKE_MARKETPLACE_KIOSK),
554
+ tx.pure.address(nftTokenId),
555
+ tx.object(coin1),
556
+ ],
557
+ typeArguments: [nftType],
558
+ });
559
+
560
+ if (!transferRequest) throw new Error('Transfer request could not be created');
561
+
562
+ tx.moveCall({
563
+ target:
564
+ '0x02be8c4a1a3cea4d3255d870d367c87838a8cc2bfe4f216a6b67b153027087a7::keepsake_royalties::confirm_transfer_with_balance',
565
+ arguments: [tx.object(keepsakeRoyaltyStrategy), tx.object(transferRequest), tx.object(balance)],
566
+ typeArguments: [nftType],
567
+ });
568
+
569
+ tx.moveCall({
570
+ target:
571
+ '0x02be8c4a1a3cea4d3255d870d367c87838a8cc2bfe4f216a6b67b153027087a7::transfer_policy::confirm_request',
572
+ arguments: [tx.object(transferPolicyId), tx.object(transferRequest)],
573
+ typeArguments: [nftType],
574
+ });
575
+
576
+ const [balanceToTransfer] = tx.moveCall({
577
+ target: '0x2::coin::from_balance',
578
+ arguments: [tx.object(balance)],
579
+ typeArguments: ['0x2::sui::SUI'],
580
+ });
581
+
582
+ if (!balanceToTransfer) throw new Error('Coin from_balance failed');
583
+
584
+ tx.transferObjects(
585
+ [tx.object(balanceToTransfer)],
586
+ tx.pure.address(addLeadingZerosAfter0x(buyer)),
587
+ );
588
+ }
589
+
590
+ export const addTocenBuyTx = ({ tx, nftTokenIds, nftType, price, coinToSplit }: TocenBuyTx) => {
591
+ const [coin] = tx.splitCoins(coinToSplit ? coinToSplit : tx.gas, [tx.pure.u64(price)]);
592
+
593
+ if (!coin) throw new Error('Coin could not be split');
594
+
595
+ tx.moveCall({
596
+ target:
597
+ '0x3605d91c559e80cf8fdeabae9abaccb0bc38f96eac0b32bf47e95a9159a5277f::tocen_marketplace::buy_cart',
598
+ arguments: [
599
+ tx.object(TOCEN_MARKETPLACE_OBJECT),
600
+ tx.pure(bcs.vector(bcs.Address).serialize(nftTokenIds)),
601
+ tx.object(coin),
602
+ ],
603
+ typeArguments: [nftType],
604
+ });
605
+ };
606
+
607
+ export async function addTradePortBuyTxHandler(txData: BuyTx) {
608
+ // If origin byte collection, add origin byte buy tx
609
+ if (txData?.listingNonce && isOriginByteCollection(txData?.transferPolicies)) {
610
+ const sharedObjects =
611
+ txData?.sharedBulkBuyingDataByNftType?.[txData?.nftType]?.sharedObjects ??
612
+ (await getSharedObjects(txData?.nftType));
613
+ await addOriginByteBuyTx({ ...txData, sharedObjects });
614
+
615
+ return;
616
+ }
617
+
618
+ // If non kiosk listing (nonce starts with "0::")
619
+ if (txData?.listingNonce?.startsWith('0::')) {
620
+ await addTradePortBuyTx(txData);
621
+ return;
622
+ }
623
+
624
+ // If kiosk listing (nonce starts with "1::")
625
+ if (txData?.listingNonce?.startsWith('1::')) {
626
+ await kioskTxWrapper({
627
+ tx: txData?.tx,
628
+ kioskClient: txData?.kioskClient,
629
+ kioskOwner: txData?.buyer,
630
+ kiosk: txData?.sellerKiosk,
631
+ shouldConvertToPersonalKiosk: true,
632
+ sharedKioskState: txData?.sharedKioskState,
633
+ async runCommands(kioskTx) {
634
+ await addKioskTradePortBuyTx({
635
+ ...txData,
636
+ kioskTx,
637
+ });
638
+
639
+ // If XOCIETY Frontier collection, take NFT out of kiosk and transfer it back to user (buyer)
640
+ if (txData?.collectionId === XOCIETY_FRONTIER_COLLECTION_ID) {
641
+ const borrowedItem = kioskTx.take({
642
+ itemId: txData?.nftTokenId,
643
+ itemType: txData?.nftType,
644
+ });
645
+
646
+ txData?.tx.transferObjects(
647
+ [txData?.tx.object(borrowedItem as any)],
648
+ txData?.tx.pure.address(addLeadingZerosAfter0x(txData?.buyer)),
649
+ );
650
+ }
651
+ },
652
+ });
653
+
654
+ // If seller kiosk is associated with an NFT strategy, apply the strategy after the buy command
655
+ const chainState = await getCollectionChainState(txData.collectionId);
656
+ const nftStrategies = (chainState?.nft_strategies ?? {}) as Record<
657
+ string,
658
+ Record<string, unknown>
659
+ >;
660
+ const normalizedSellerKiosk = normalizeSuiObjectId(txData.sellerKiosk);
661
+ for (const [strategyFtType, strategyData] of Object.entries(nftStrategies)) {
662
+ const strategyKioskId = (strategyData as { kiosk_id: string }).kiosk_id ?? '0x0';
663
+ if (normalizeSuiObjectId(strategyKioskId) === normalizedSellerKiosk) {
664
+ await applyFtStrategy({
665
+ collectionId: txData.collectionId,
666
+ strategyFtType,
667
+ tx: txData.tx,
668
+ collectionChainState: chainState,
669
+ });
670
+ break;
671
+ }
672
+ }
673
+
674
+ return;
675
+ }
676
+
677
+ // Legacy contract logic below
678
+ if (txData.listingNonce?.startsWith('0x')) {
679
+ const response = await txData?.suiClient.getObject({
680
+ objectId: txData.listingNonce,
681
+ include: {},
682
+ });
683
+
684
+ if (response.object.type === NON_KIOSK_LISTING_NONCE_TYPE) {
685
+ const sharedObjects =
686
+ txData?.sharedBulkBuyingDataByNftType?.[txData?.nftType]?.sharedObjects ??
687
+ (await getSharedObjects(txData?.nftType));
688
+ addLegacyTradePortBuyTx({ ...txData, sharedObjects });
689
+ return;
690
+ }
691
+ }
692
+
693
+ // If legacy kiosk listing
694
+ if (txData?.sellerKiosk) {
695
+ return kioskTxWrapper({
696
+ tx: txData?.tx,
697
+ kioskClient: txData?.kioskClient,
698
+ kioskOwner: txData?.buyer,
699
+ kiosk: txData?.sellerKiosk,
700
+ shouldConvertToPersonalKiosk: true,
701
+ sharedKioskState: txData?.sharedKioskState,
702
+ async runCommands(kioskTx) {
703
+ await addLegacyTradeportKioskBuyTx({
704
+ ...txData,
705
+ kioskTx,
706
+ });
707
+ },
708
+ });
709
+ }
710
+
711
+ const sharedObjects =
712
+ txData?.sharedBulkBuyingDataByNftType?.[txData?.nftType]?.sharedObjects ??
713
+ (await getSharedObjects(txData?.nftType));
714
+
715
+ // If legacy non kiosk listing
716
+ addLegacyTradePortBuyTx({ ...txData, sharedObjects });
717
+ }
718
+
719
+ export async function addHyperspaceBuyTxHandler(txData: BuyTx) {
720
+ if (txData?.listingNonce && isOriginByteCollection(txData?.transferPolicies)) {
721
+ const sharedObjects =
722
+ txData?.sharedBulkBuyingDataByNftType?.[txData?.nftType]?.sharedObjects ??
723
+ (await getSharedObjects(txData?.nftType));
724
+ await addOriginByteBuyTx({ ...txData, sharedObjects });
725
+
726
+ return;
727
+ }
728
+
729
+ return kioskTxWrapper({
730
+ tx: txData?.tx,
731
+ kioskClient: txData?.kioskClient,
732
+ kioskOwner: txData?.buyer,
733
+ kiosk: txData?.sellerKiosk,
734
+ shouldConvertToPersonalKiosk: true,
735
+ sharedKioskState: txData?.sharedKioskState,
736
+ async runCommands(kioskTx) {
737
+ await addHyperspaceKioskBuyTx({
738
+ ...txData,
739
+ kioskTx,
740
+ });
741
+ },
742
+ });
743
+ }
744
+
745
+ export async function addBluemoveBuyTxHandler(txData: BuyTx) {
746
+ if (txData?.listingNonce && isOriginByteCollection(txData?.transferPolicies)) {
747
+ const sharedObjects =
748
+ txData?.sharedBulkBuyingDataByNftType?.[txData?.nftType]?.sharedObjects ??
749
+ (await getSharedObjects(txData?.nftType));
750
+ await addOriginByteBuyTx({ ...txData, sharedObjects });
751
+
752
+ return;
753
+ }
754
+
755
+ if (txData?.sellerKiosk) {
756
+ return kioskTxWrapper({
757
+ tx: txData?.tx,
758
+ kioskClient: txData?.kioskClient,
759
+ kioskOwner: txData?.buyer,
760
+ kiosk: txData?.sellerKiosk,
761
+ shouldConvertToPersonalKiosk: true,
762
+ sharedKioskState: txData?.sharedKioskState,
763
+ async runCommands(kioskTx) {
764
+ await addBluemoveKioskBuyTx({
765
+ ...txData,
766
+ kioskTx,
767
+ });
768
+ },
769
+ });
770
+ }
771
+
772
+ addBluemoveBuyTx(txData);
773
+ }
774
+
775
+ export async function addClutchyBuyTxHandler(txData: BuyTx) {
776
+ const sharedObjects =
777
+ txData?.sharedBulkBuyingDataByNftType?.[txData?.nftType]?.sharedObjects ??
778
+ (await getSharedObjects(txData?.nftType));
779
+ await addOriginByteBuyTx({ ...txData, sharedObjects });
780
+ }
781
+
782
+ export async function addSouffl3BuyTxHandler(txData: BuyTx) {
783
+ await addSouffl3BuyTx(txData);
784
+ }
785
+
786
+ export async function addSomisBuyTxHandler(txData: BuyTx) {
787
+ if (txData?.sharedObjects?.marketplace) {
788
+ addSomisBuyTx(txData);
789
+ return;
790
+ }
791
+
792
+ return addOriginByteBuyTx(txData);
793
+ }
794
+
795
+ export async function addKeepsakeBuyTxHandler(txData: BuyTx) {
796
+ const commission = await getCommissionByNftContractId(txData?.nftContractId);
797
+
798
+ await addKeepsakeBuyTx({
799
+ ...txData,
800
+ royalty: commission?.royalty,
801
+ });
802
+ }
803
+
804
+ export function addTocenBuyTxHandler(txData: TocenBuyTx) {
805
+ addTocenBuyTx(txData);
806
+ }