@quantabit/market-sdk 1.0.0
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/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/index.cjs +1077 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +1051 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +96 -0
- package/types/index.d.ts +67 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1077 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var sdkConfig = require('@quantabit/sdk-config');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Market SDK - API 客户端
|
|
8
|
+
* 市场交易系统后端接口封装
|
|
9
|
+
*
|
|
10
|
+
* 使用 BaseApiClient 基类简化代码
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 市场 API 客户端
|
|
16
|
+
*/
|
|
17
|
+
class MarketApiClient extends sdkConfig.BaseApiClient {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
super('/market', config);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ============ 市场浏览 ============
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 获取市场商品列表
|
|
26
|
+
* @param {Object} params - 查询参数
|
|
27
|
+
*/
|
|
28
|
+
async getListings(params = {}) {
|
|
29
|
+
return this.get('/listings', params);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 兼容旧 Hook 命名
|
|
34
|
+
*/
|
|
35
|
+
async getProducts(params = {}) {
|
|
36
|
+
return this.getListings(params);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 获取商品详情
|
|
41
|
+
* @param {string} listingId - 商品 ID
|
|
42
|
+
*/
|
|
43
|
+
async getListing(listingId) {
|
|
44
|
+
return this.get(`/listings/${listingId}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 搜索商品
|
|
49
|
+
* @param {string} keyword - 关键词
|
|
50
|
+
* @param {Object} params - 查询参数
|
|
51
|
+
*/
|
|
52
|
+
async searchListings(keyword, params = {}) {
|
|
53
|
+
return this.get('/listings/search', {
|
|
54
|
+
keyword,
|
|
55
|
+
...params
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 获取分类
|
|
61
|
+
*/
|
|
62
|
+
async getCategories() {
|
|
63
|
+
return this.get('/categories');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 按分类获取商品
|
|
68
|
+
* @param {string} categoryId - 分类 ID
|
|
69
|
+
* @param {Object} params - 查询参数
|
|
70
|
+
*/
|
|
71
|
+
async getListingsByCategory(categoryId, params = {}) {
|
|
72
|
+
return this.get(`/categories/${categoryId}/listings`, params);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ============ 挂单管理 ============
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 创建挂单
|
|
79
|
+
* @param {Object} listing - 挂单数据
|
|
80
|
+
*/
|
|
81
|
+
async createListing(listing) {
|
|
82
|
+
return this.post('/listings', listing);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 更新挂单
|
|
87
|
+
* @param {string} listingId - 挂单 ID
|
|
88
|
+
* @param {Object} updates - 更新数据
|
|
89
|
+
*/
|
|
90
|
+
async updateListing(listingId, updates) {
|
|
91
|
+
return this.put(`/listings/${listingId}`, updates);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 取消挂单
|
|
96
|
+
* @param {string} listingId - 挂单 ID
|
|
97
|
+
*/
|
|
98
|
+
async cancelListing(listingId) {
|
|
99
|
+
return this.post(`/listings/${listingId}/cancel`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 获取我的挂单
|
|
104
|
+
* @param {Object} params - 查询参数
|
|
105
|
+
*/
|
|
106
|
+
async getMyListings(params = {}) {
|
|
107
|
+
return this.get('/my/listings', params);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ============ 交易操作 ============
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 购买
|
|
114
|
+
* @param {string} listingId - 挂单 ID
|
|
115
|
+
* @param {Object} options - 购买选项
|
|
116
|
+
*/
|
|
117
|
+
async buy(listingId, options = {}) {
|
|
118
|
+
return this.post(`/listings/${listingId}/buy`, options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 兼容旧 Hook 命名
|
|
123
|
+
*/
|
|
124
|
+
async purchase(productId, quantity = 1, options = {}) {
|
|
125
|
+
return this.buy(productId, {
|
|
126
|
+
quantity,
|
|
127
|
+
...options
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 出价
|
|
133
|
+
* @param {string} listingId - 挂单 ID
|
|
134
|
+
* @param {Object} offer - 出价数据
|
|
135
|
+
*/
|
|
136
|
+
async makeOffer(listingId, offer) {
|
|
137
|
+
return this.post(`/listings/${listingId}/offer`, offer);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 接受出价
|
|
142
|
+
* @param {string} listingId - 挂单 ID
|
|
143
|
+
* @param {string} offerId - 出价 ID
|
|
144
|
+
*/
|
|
145
|
+
async acceptOffer(listingId, offerId) {
|
|
146
|
+
return this.post(`/listings/${listingId}/offers/${offerId}/accept`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 拒绝出价
|
|
151
|
+
* @param {string} listingId - 挂单 ID
|
|
152
|
+
* @param {string} offerId - 出价 ID
|
|
153
|
+
*/
|
|
154
|
+
async rejectOffer(listingId, offerId) {
|
|
155
|
+
return this.post(`/listings/${listingId}/offers/${offerId}/reject`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 获取商品出价
|
|
160
|
+
* @param {string} listingId - 挂单 ID
|
|
161
|
+
*/
|
|
162
|
+
async getOffers(listingId) {
|
|
163
|
+
return this.get(`/listings/${listingId}/offers`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ============ 竞拍 ============
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 获取拍卖列表
|
|
170
|
+
* @param {Object} params - 查询参数
|
|
171
|
+
*/
|
|
172
|
+
async getAuctions(params = {}) {
|
|
173
|
+
return this.get('/auctions', params);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 参与竞拍
|
|
178
|
+
* @param {string} auctionId - 拍卖 ID
|
|
179
|
+
* @param {number} amount - 出价金额
|
|
180
|
+
*/
|
|
181
|
+
async placeBid(auctionId, amount) {
|
|
182
|
+
return this.post(`/auctions/${auctionId}/bid`, {
|
|
183
|
+
amount
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 获取竞拍历史
|
|
189
|
+
* @param {string} auctionId - 拍卖 ID
|
|
190
|
+
*/
|
|
191
|
+
async getBidHistory(auctionId) {
|
|
192
|
+
return this.get(`/auctions/${auctionId}/bids`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ============ 收藏/关注 ============
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 收藏商品
|
|
199
|
+
* @param {string} listingId - 挂单 ID
|
|
200
|
+
*/
|
|
201
|
+
async favorite(listingId) {
|
|
202
|
+
return this.post(`/listings/${listingId}/favorite`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 取消收藏
|
|
207
|
+
* @param {string} listingId - 挂单 ID
|
|
208
|
+
*/
|
|
209
|
+
async unfavorite(listingId) {
|
|
210
|
+
return this.delete(`/listings/${listingId}/favorite`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 获取收藏列表
|
|
215
|
+
* @param {Object} params - 查询参数
|
|
216
|
+
*/
|
|
217
|
+
async getFavorites(params = {}) {
|
|
218
|
+
return this.get('/my/favorites', params);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 设置价格提醒
|
|
223
|
+
* @param {string} listingId - 挂单 ID
|
|
224
|
+
* @param {number} targetPrice - 目标价格
|
|
225
|
+
*/
|
|
226
|
+
async setPriceAlert(listingId, targetPrice) {
|
|
227
|
+
return this.post(`/listings/${listingId}/price-alert`, {
|
|
228
|
+
target_price: targetPrice
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ============ 订单/交易记录 ============
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 获取我的交易记录
|
|
236
|
+
* @param {Object} params - 查询参数
|
|
237
|
+
*/
|
|
238
|
+
async getMyTransactions(params = {}) {
|
|
239
|
+
return this.get('/my/transactions', params);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 获取交易详情
|
|
244
|
+
* @param {string} transactionId - 交易 ID
|
|
245
|
+
*/
|
|
246
|
+
async getTransaction(transactionId) {
|
|
247
|
+
return this.get(`/transactions/${transactionId}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ============ 链上证明 ============
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 提交挂单链上交易哈希
|
|
254
|
+
*/
|
|
255
|
+
async submitListingChainTx(listingId, txHash, data = {}) {
|
|
256
|
+
return this.post(`/listings/${listingId}/chain/submit`, {
|
|
257
|
+
tx_hash: txHash,
|
|
258
|
+
chain: 'qbit',
|
|
259
|
+
network: 'mainnet',
|
|
260
|
+
...data
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 提交交易链上交易哈希
|
|
266
|
+
*/
|
|
267
|
+
async submitTransactionChainTx(transactionId, txHash, data = {}) {
|
|
268
|
+
return this.post(`/transactions/${transactionId}/chain/submit`, {
|
|
269
|
+
tx_hash: txHash,
|
|
270
|
+
chain: 'qbit',
|
|
271
|
+
network: 'mainnet',
|
|
272
|
+
...data
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async getListingChainStatus(listingId) {
|
|
276
|
+
return this.get(`/listings/${listingId}/chain/status`);
|
|
277
|
+
}
|
|
278
|
+
async getTransactionChainStatus(transactionId) {
|
|
279
|
+
return this.get(`/transactions/${transactionId}/chain/status`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ============ 市场统计 ============
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 获取市场统计
|
|
286
|
+
*/
|
|
287
|
+
async getMarketStats() {
|
|
288
|
+
return this.get('/stats');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 获取价格趋势
|
|
293
|
+
* @param {string} itemType - 物品类型
|
|
294
|
+
* @param {Object} params - 查询参数
|
|
295
|
+
*/
|
|
296
|
+
async getPriceTrend(itemType, params = {}) {
|
|
297
|
+
return this.get('/trends', {
|
|
298
|
+
item_type: itemType,
|
|
299
|
+
...params
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 创建默认实例
|
|
305
|
+
const marketApi = new MarketApiClient();
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Market SDK - 国际化 i18n
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
|
|
312
|
+
const messages = {
|
|
313
|
+
zh: {
|
|
314
|
+
loading: '加载中...',
|
|
315
|
+
market: '市场',
|
|
316
|
+
marketplace: '交易市场',
|
|
317
|
+
// 商品类型
|
|
318
|
+
typeNFT: 'NFT',
|
|
319
|
+
typeToken: '代币',
|
|
320
|
+
typeGoods: '实物商品',
|
|
321
|
+
typeDigital: '数字商品',
|
|
322
|
+
// 交易类型
|
|
323
|
+
listingFixed: '一口价',
|
|
324
|
+
listingAuction: '拍卖',
|
|
325
|
+
listingOffer: '报价出售',
|
|
326
|
+
// 状态
|
|
327
|
+
statusListed: '在售',
|
|
328
|
+
statusSold: '已售出',
|
|
329
|
+
statusCancelled: '已下架',
|
|
330
|
+
statusExpired: '已过期',
|
|
331
|
+
// 操作
|
|
332
|
+
buy: '购买',
|
|
333
|
+
buyNow: '立即购买',
|
|
334
|
+
placeBid: '出价',
|
|
335
|
+
makeOffer: '发起报价',
|
|
336
|
+
list: '上架',
|
|
337
|
+
delist: '下架',
|
|
338
|
+
edit: '编辑',
|
|
339
|
+
// 价格
|
|
340
|
+
price: '价格',
|
|
341
|
+
floorPrice: '地板价',
|
|
342
|
+
highestBid: '最高出价',
|
|
343
|
+
currentBid: '当前出价',
|
|
344
|
+
minBid: '最低出价',
|
|
345
|
+
buyoutPrice: '一口价',
|
|
346
|
+
// 拍卖
|
|
347
|
+
auction: '拍卖',
|
|
348
|
+
startPrice: '起拍价',
|
|
349
|
+
endTime: '结束时间',
|
|
350
|
+
bids: '出价记录',
|
|
351
|
+
noBids: '暂无出价',
|
|
352
|
+
bidSuccess: '出价成功',
|
|
353
|
+
// 记录
|
|
354
|
+
history: '交易记录',
|
|
355
|
+
myListings: '我的挂单',
|
|
356
|
+
myPurchases: '我的购买',
|
|
357
|
+
noListings: '暂无商品',
|
|
358
|
+
// 筛选
|
|
359
|
+
filter: '筛选',
|
|
360
|
+
sortBy: '排序',
|
|
361
|
+
priceHighToLow: '价格从高到低',
|
|
362
|
+
priceLowToHigh: '价格从低到高',
|
|
363
|
+
recentlyListed: '最新上架',
|
|
364
|
+
endingSoon: '即将结束',
|
|
365
|
+
// 费用
|
|
366
|
+
fee: '手续费',
|
|
367
|
+
royalty: '版税',
|
|
368
|
+
total: '合计'
|
|
369
|
+
},
|
|
370
|
+
en: {
|
|
371
|
+
loading: 'Loading...',
|
|
372
|
+
market: 'Market',
|
|
373
|
+
marketplace: 'Marketplace',
|
|
374
|
+
typeNFT: 'NFT',
|
|
375
|
+
typeToken: 'Token',
|
|
376
|
+
typeGoods: 'Physical Goods',
|
|
377
|
+
typeDigital: 'Digital Goods',
|
|
378
|
+
listingFixed: 'Fixed Price',
|
|
379
|
+
listingAuction: 'Auction',
|
|
380
|
+
listingOffer: 'Open to Offers',
|
|
381
|
+
statusListed: 'Listed',
|
|
382
|
+
statusSold: 'Sold',
|
|
383
|
+
statusCancelled: 'Cancelled',
|
|
384
|
+
statusExpired: 'Expired',
|
|
385
|
+
buy: 'Buy',
|
|
386
|
+
buyNow: 'Buy Now',
|
|
387
|
+
placeBid: 'Place Bid',
|
|
388
|
+
makeOffer: 'Make Offer',
|
|
389
|
+
list: 'List',
|
|
390
|
+
delist: 'Delist',
|
|
391
|
+
edit: 'Edit',
|
|
392
|
+
price: 'Price',
|
|
393
|
+
floorPrice: 'Floor Price',
|
|
394
|
+
highestBid: 'Highest Bid',
|
|
395
|
+
currentBid: 'Current Bid',
|
|
396
|
+
minBid: 'Min Bid',
|
|
397
|
+
buyoutPrice: 'Buy Now Price',
|
|
398
|
+
auction: 'Auction',
|
|
399
|
+
startPrice: 'Starting Price',
|
|
400
|
+
endTime: 'Ends In',
|
|
401
|
+
bids: 'Bids',
|
|
402
|
+
noBids: 'No bids yet',
|
|
403
|
+
bidSuccess: 'Bid placed',
|
|
404
|
+
history: 'History',
|
|
405
|
+
myListings: 'My Listings',
|
|
406
|
+
myPurchases: 'My Purchases',
|
|
407
|
+
noListings: 'No listings',
|
|
408
|
+
filter: 'Filter',
|
|
409
|
+
sortBy: 'Sort By',
|
|
410
|
+
priceHighToLow: 'Price: High to Low',
|
|
411
|
+
priceLowToHigh: 'Price: Low to High',
|
|
412
|
+
recentlyListed: 'Recently Listed',
|
|
413
|
+
endingSoon: 'Ending Soon',
|
|
414
|
+
fee: 'Fee',
|
|
415
|
+
royalty: 'Royalty',
|
|
416
|
+
total: 'Total'
|
|
417
|
+
},
|
|
418
|
+
ja: {
|
|
419
|
+
loading: '読み込み中...',
|
|
420
|
+
market: 'マーケット',
|
|
421
|
+
marketplace: 'マーケットプレイス',
|
|
422
|
+
typeNFT: 'NFT',
|
|
423
|
+
typeToken: 'トークン',
|
|
424
|
+
typeGoods: '実物商品',
|
|
425
|
+
typeDigital: 'デジタル商品',
|
|
426
|
+
listingFixed: '即決価格',
|
|
427
|
+
listingAuction: 'オークション',
|
|
428
|
+
listingOffer: 'オファー受付中',
|
|
429
|
+
statusListed: '出品中',
|
|
430
|
+
statusSold: '売却済み',
|
|
431
|
+
statusCancelled: 'キャンセル',
|
|
432
|
+
statusExpired: '期限切れ',
|
|
433
|
+
buy: '購入',
|
|
434
|
+
buyNow: '今すぐ購入',
|
|
435
|
+
placeBid: '入札する',
|
|
436
|
+
makeOffer: 'オファーする',
|
|
437
|
+
list: '出品',
|
|
438
|
+
delist: '出品取り消し',
|
|
439
|
+
edit: '編集',
|
|
440
|
+
price: '価格',
|
|
441
|
+
floorPrice: 'フロア価格',
|
|
442
|
+
highestBid: '最高入札額',
|
|
443
|
+
currentBid: '現在の入札額',
|
|
444
|
+
minBid: '最低入札額',
|
|
445
|
+
buyoutPrice: '即決価格',
|
|
446
|
+
auction: 'オークション',
|
|
447
|
+
startPrice: '開始価格',
|
|
448
|
+
endTime: '終了まで',
|
|
449
|
+
bids: '入札',
|
|
450
|
+
noBids: '入札はまだありません',
|
|
451
|
+
bidSuccess: '入札しました',
|
|
452
|
+
history: '履歴',
|
|
453
|
+
myListings: 'マイ出品',
|
|
454
|
+
myPurchases: 'マイ購入',
|
|
455
|
+
noListings: '出品なし',
|
|
456
|
+
filter: 'フィルター',
|
|
457
|
+
sortBy: '並び替え',
|
|
458
|
+
priceHighToLow: '価格: 高から低',
|
|
459
|
+
priceLowToHigh: '価格: 低から高',
|
|
460
|
+
recentlyListed: '新着出品',
|
|
461
|
+
endingSoon: '終了間近',
|
|
462
|
+
fee: '手数料',
|
|
463
|
+
royalty: 'ロイヤリティ',
|
|
464
|
+
total: '合計'
|
|
465
|
+
},
|
|
466
|
+
ko: {
|
|
467
|
+
loading: '로딩 중...',
|
|
468
|
+
market: '마켓',
|
|
469
|
+
marketplace: '마켓플레이스',
|
|
470
|
+
typeNFT: 'NFT',
|
|
471
|
+
typeToken: '토큰',
|
|
472
|
+
typeGoods: '실물 상품',
|
|
473
|
+
typeDigital: '디지털 상품',
|
|
474
|
+
listingFixed: '고정 가격',
|
|
475
|
+
listingAuction: '경매',
|
|
476
|
+
listingOffer: '제안 가능',
|
|
477
|
+
statusListed: '판매 중',
|
|
478
|
+
statusSold: '판매됨',
|
|
479
|
+
statusCancelled: '취소됨',
|
|
480
|
+
statusExpired: '만료됨',
|
|
481
|
+
buy: '구매',
|
|
482
|
+
buyNow: '바로 구매',
|
|
483
|
+
placeBid: '입찰하기',
|
|
484
|
+
makeOffer: '제안하기',
|
|
485
|
+
list: '판매 등록',
|
|
486
|
+
delist: '등록 취소',
|
|
487
|
+
edit: '편집',
|
|
488
|
+
price: '가격',
|
|
489
|
+
floorPrice: '최저가',
|
|
490
|
+
highestBid: '최고 입찰가',
|
|
491
|
+
currentBid: '현재 입찰가',
|
|
492
|
+
minBid: '최소 입찰가',
|
|
493
|
+
buyoutPrice: '즉시 구매가',
|
|
494
|
+
auction: '경매',
|
|
495
|
+
startPrice: '시작 가격',
|
|
496
|
+
endTime: '종료 시간',
|
|
497
|
+
bids: '입찰',
|
|
498
|
+
noBids: '입찰 없음',
|
|
499
|
+
bidSuccess: '입찰 성공',
|
|
500
|
+
history: '내역',
|
|
501
|
+
myListings: '내 판매 목록',
|
|
502
|
+
myPurchases: '내 구매 목록',
|
|
503
|
+
noListings: '판매 목록 없음',
|
|
504
|
+
filter: '필터',
|
|
505
|
+
sortBy: '정렬',
|
|
506
|
+
priceHighToLow: '가격: 높은 순',
|
|
507
|
+
priceLowToHigh: '가격: 낮은 순',
|
|
508
|
+
recentlyListed: '최근 등록',
|
|
509
|
+
endingSoon: '마감 임박',
|
|
510
|
+
fee: '수수료',
|
|
511
|
+
royalty: '로열티',
|
|
512
|
+
total: '합계'
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
let currentLanguage = 'zh';
|
|
516
|
+
function setLanguage(lang) {
|
|
517
|
+
if (SUPPORTED_LANGUAGES.includes(lang)) {
|
|
518
|
+
currentLanguage = lang;
|
|
519
|
+
if (typeof window !== 'undefined') {
|
|
520
|
+
localStorage.setItem('qbit_did_market_language', lang);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function getLanguage() {
|
|
525
|
+
if (typeof window !== 'undefined') {
|
|
526
|
+
const saved = localStorage.getItem('qbit_did_market_language');
|
|
527
|
+
if (saved && SUPPORTED_LANGUAGES.includes(saved)) {
|
|
528
|
+
currentLanguage = saved;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return currentLanguage;
|
|
532
|
+
}
|
|
533
|
+
function t(key, params) {
|
|
534
|
+
const msgs = messages[getLanguage()] || messages.zh;
|
|
535
|
+
let text = msgs[key] || key;
|
|
536
|
+
if (params) {
|
|
537
|
+
Object.keys(params).forEach(k => {
|
|
538
|
+
text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), params[k]);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return text;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Market SDK - Context Provider
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
const MarketContext = /*#__PURE__*/React.createContext(null);
|
|
549
|
+
function MarketProvider({
|
|
550
|
+
children,
|
|
551
|
+
apiUrl,
|
|
552
|
+
token,
|
|
553
|
+
language = 'zh',
|
|
554
|
+
onPurchase,
|
|
555
|
+
onBid,
|
|
556
|
+
onError
|
|
557
|
+
}) {
|
|
558
|
+
const [listings, setListings] = React.useState([]);
|
|
559
|
+
const [currentListing, setCurrentListing] = React.useState(null);
|
|
560
|
+
const [myListings, setMyListings] = React.useState([]);
|
|
561
|
+
const [myPurchases, setMyPurchases] = React.useState([]);
|
|
562
|
+
const [loading, setLoading] = React.useState(false);
|
|
563
|
+
const [error, setError] = React.useState(null);
|
|
564
|
+
React.useEffect(() => {
|
|
565
|
+
if (apiUrl) marketApi.setBaseUrl(apiUrl);
|
|
566
|
+
if (token) marketApi.setToken(token);
|
|
567
|
+
if (language) setLanguage(language);
|
|
568
|
+
}, [apiUrl, token, language]);
|
|
569
|
+
const handleError = React.useCallback(err => {
|
|
570
|
+
setError(err.message);
|
|
571
|
+
onError?.(err);
|
|
572
|
+
}, [onError]);
|
|
573
|
+
|
|
574
|
+
// ============ 市场浏览 ============
|
|
575
|
+
|
|
576
|
+
const loadListings = React.useCallback(async (params = {}) => {
|
|
577
|
+
setLoading(true);
|
|
578
|
+
try {
|
|
579
|
+
const result = await marketApi.getListings(params);
|
|
580
|
+
const items = result.items || result.data || result || [];
|
|
581
|
+
setListings(items);
|
|
582
|
+
return items;
|
|
583
|
+
} catch (err) {
|
|
584
|
+
handleError(err);
|
|
585
|
+
throw err;
|
|
586
|
+
} finally {
|
|
587
|
+
setLoading(false);
|
|
588
|
+
}
|
|
589
|
+
}, [handleError]);
|
|
590
|
+
const loadListingDetail = React.useCallback(async listingId => {
|
|
591
|
+
setLoading(true);
|
|
592
|
+
try {
|
|
593
|
+
const result = await marketApi.getListingDetail(listingId);
|
|
594
|
+
setCurrentListing(result);
|
|
595
|
+
return result;
|
|
596
|
+
} catch (err) {
|
|
597
|
+
handleError(err);
|
|
598
|
+
throw err;
|
|
599
|
+
} finally {
|
|
600
|
+
setLoading(false);
|
|
601
|
+
}
|
|
602
|
+
}, [handleError]);
|
|
603
|
+
|
|
604
|
+
// ============ 交易操作 ============
|
|
605
|
+
|
|
606
|
+
const buy = React.useCallback(async listingId => {
|
|
607
|
+
setLoading(true);
|
|
608
|
+
try {
|
|
609
|
+
const result = await marketApi.buy(listingId);
|
|
610
|
+
onPurchase?.(result);
|
|
611
|
+
return result;
|
|
612
|
+
} catch (err) {
|
|
613
|
+
handleError(err);
|
|
614
|
+
throw err;
|
|
615
|
+
} finally {
|
|
616
|
+
setLoading(false);
|
|
617
|
+
}
|
|
618
|
+
}, [handleError, onPurchase]);
|
|
619
|
+
const placeBid = React.useCallback(async (listingId, amount) => {
|
|
620
|
+
setLoading(true);
|
|
621
|
+
try {
|
|
622
|
+
const result = await marketApi.placeBid(listingId, amount);
|
|
623
|
+
onBid?.(result);
|
|
624
|
+
return result;
|
|
625
|
+
} catch (err) {
|
|
626
|
+
handleError(err);
|
|
627
|
+
throw err;
|
|
628
|
+
} finally {
|
|
629
|
+
setLoading(false);
|
|
630
|
+
}
|
|
631
|
+
}, [handleError, onBid]);
|
|
632
|
+
|
|
633
|
+
// ============ 我的 ============
|
|
634
|
+
|
|
635
|
+
const loadMyListings = React.useCallback(async (params = {}) => {
|
|
636
|
+
try {
|
|
637
|
+
const result = await marketApi.getMyListings(params);
|
|
638
|
+
const items = result.items || result.data || result || [];
|
|
639
|
+
setMyListings(items);
|
|
640
|
+
return items;
|
|
641
|
+
} catch (err) {
|
|
642
|
+
handleError(err);
|
|
643
|
+
}
|
|
644
|
+
}, [handleError]);
|
|
645
|
+
const loadMyPurchases = React.useCallback(async (params = {}) => {
|
|
646
|
+
try {
|
|
647
|
+
const result = await marketApi.getMyPurchases(params);
|
|
648
|
+
const items = result.items || result.data || result || [];
|
|
649
|
+
setMyPurchases(items);
|
|
650
|
+
return items;
|
|
651
|
+
} catch (err) {
|
|
652
|
+
handleError(err);
|
|
653
|
+
}
|
|
654
|
+
}, [handleError]);
|
|
655
|
+
const value = {
|
|
656
|
+
listings,
|
|
657
|
+
currentListing,
|
|
658
|
+
myListings,
|
|
659
|
+
myPurchases,
|
|
660
|
+
loading,
|
|
661
|
+
error,
|
|
662
|
+
loadListings,
|
|
663
|
+
loadListingDetail,
|
|
664
|
+
buy,
|
|
665
|
+
placeBid,
|
|
666
|
+
loadMyListings,
|
|
667
|
+
loadMyPurchases,
|
|
668
|
+
api: marketApi
|
|
669
|
+
};
|
|
670
|
+
return /*#__PURE__*/React.createElement(MarketContext.Provider, {
|
|
671
|
+
value: value
|
|
672
|
+
}, children);
|
|
673
|
+
}
|
|
674
|
+
function useMarket() {
|
|
675
|
+
const context = React.useContext(MarketContext);
|
|
676
|
+
if (!context) {
|
|
677
|
+
throw new Error('useMarket must be used within a MarketProvider');
|
|
678
|
+
}
|
|
679
|
+
return context;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Market SDK - 类型定义
|
|
684
|
+
*/
|
|
685
|
+
|
|
686
|
+
// 商品类型
|
|
687
|
+
const ItemType = {
|
|
688
|
+
NFT: 'nft',
|
|
689
|
+
// NFT
|
|
690
|
+
TOKEN: 'token',
|
|
691
|
+
// 代币
|
|
692
|
+
GOODS: 'goods',
|
|
693
|
+
// 实物商品
|
|
694
|
+
DIGITAL: 'digital' // 数字商品
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// 挂单类型
|
|
698
|
+
const ListingType = {
|
|
699
|
+
FIXED: 'fixed',
|
|
700
|
+
// 一口价
|
|
701
|
+
AUCTION: 'auction',
|
|
702
|
+
// 拍卖
|
|
703
|
+
OFFER: 'offer' // 报价
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
// 挂单状态
|
|
707
|
+
const ListingStatus = {
|
|
708
|
+
ACTIVE: 'active',
|
|
709
|
+
// 活跃
|
|
710
|
+
SOLD: 'sold',
|
|
711
|
+
// 已售出
|
|
712
|
+
CANCELLED: 'cancelled',
|
|
713
|
+
// 已取消
|
|
714
|
+
EXPIRED: 'expired' // 已过期
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
// 出价状态
|
|
718
|
+
const BidStatus = {
|
|
719
|
+
PENDING: 'pending',
|
|
720
|
+
ACCEPTED: 'accepted',
|
|
721
|
+
REJECTED: 'rejected',
|
|
722
|
+
OUTBID: 'outbid',
|
|
723
|
+
// 被超越
|
|
724
|
+
CANCELLED: 'cancelled'
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* 创建默认挂单
|
|
729
|
+
*/
|
|
730
|
+
function createDefaultListing(overrides = {}) {
|
|
731
|
+
return {
|
|
732
|
+
id: null,
|
|
733
|
+
seller_did: null,
|
|
734
|
+
seller_name: '',
|
|
735
|
+
item_type: ItemType.NFT,
|
|
736
|
+
listing_type: ListingType.FIXED,
|
|
737
|
+
status: ListingStatus.ACTIVE,
|
|
738
|
+
// 商品信息
|
|
739
|
+
item_id: null,
|
|
740
|
+
item_name: '',
|
|
741
|
+
item_image: null,
|
|
742
|
+
item_description: '',
|
|
743
|
+
// 价格
|
|
744
|
+
price: 0,
|
|
745
|
+
currency: 'ETH',
|
|
746
|
+
start_price: 0,
|
|
747
|
+
// 拍卖起拍价
|
|
748
|
+
buyout_price: null,
|
|
749
|
+
// 拍卖一口价
|
|
750
|
+
min_bid_increment: 0,
|
|
751
|
+
// 最低加价
|
|
752
|
+
// 拍卖
|
|
753
|
+
highest_bid: 0,
|
|
754
|
+
highest_bidder: null,
|
|
755
|
+
bid_count: 0,
|
|
756
|
+
// 时间
|
|
757
|
+
start_at: null,
|
|
758
|
+
end_at: null,
|
|
759
|
+
created_at: null,
|
|
760
|
+
// 费用
|
|
761
|
+
platform_fee: 0,
|
|
762
|
+
// 平台手续费率
|
|
763
|
+
royalty_fee: 0,
|
|
764
|
+
// 版税率
|
|
765
|
+
...overrides
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* 创建默认出价
|
|
771
|
+
*/
|
|
772
|
+
function createDefaultBid(overrides = {}) {
|
|
773
|
+
return {
|
|
774
|
+
id: null,
|
|
775
|
+
listing_id: null,
|
|
776
|
+
bidder_did: null,
|
|
777
|
+
bidder_name: '',
|
|
778
|
+
amount: 0,
|
|
779
|
+
currency: 'ETH',
|
|
780
|
+
status: BidStatus.PENDING,
|
|
781
|
+
created_at: null,
|
|
782
|
+
...overrides
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* 格式化价格
|
|
788
|
+
*/
|
|
789
|
+
function formatPrice(amount, currency = 'ETH') {
|
|
790
|
+
const symbols = {
|
|
791
|
+
ETH: 'Ξ',
|
|
792
|
+
BTC: '₿',
|
|
793
|
+
CNY: '¥',
|
|
794
|
+
USD: '$',
|
|
795
|
+
USDT: 'USDT '
|
|
796
|
+
};
|
|
797
|
+
const symbol = symbols[currency] || '';
|
|
798
|
+
return `${symbol}${parseFloat(amount)}`;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* 计算用户应付金额(含手续费)
|
|
803
|
+
*/
|
|
804
|
+
function calculateTotalPrice(listing) {
|
|
805
|
+
const price = listing.highest_bid || listing.price || listing.start_price;
|
|
806
|
+
const fee = price * (listing.platform_fee || 0);
|
|
807
|
+
return price + fee;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* 检查拍卖是否结束
|
|
812
|
+
*/
|
|
813
|
+
function isAuctionEnded(listing) {
|
|
814
|
+
if (listing.status !== ListingStatus.ACTIVE) return true;
|
|
815
|
+
if (listing.end_at && new Date(listing.end_at) < new Date()) return true;
|
|
816
|
+
return false;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* 获取拍卖剩余时间
|
|
821
|
+
*/
|
|
822
|
+
function getTimeRemaining(endAt) {
|
|
823
|
+
if (!endAt) return null;
|
|
824
|
+
const now = new Date();
|
|
825
|
+
const end = new Date(endAt);
|
|
826
|
+
const diff = end - now;
|
|
827
|
+
if (diff <= 0) return {
|
|
828
|
+
ended: true,
|
|
829
|
+
days: 0,
|
|
830
|
+
hours: 0,
|
|
831
|
+
minutes: 0,
|
|
832
|
+
seconds: 0
|
|
833
|
+
};
|
|
834
|
+
return {
|
|
835
|
+
ended: false,
|
|
836
|
+
days: Math.floor(diff / 86400000),
|
|
837
|
+
hours: Math.floor(diff % 86400000 / 3600000),
|
|
838
|
+
minutes: Math.floor(diff % 3600000 / 60000),
|
|
839
|
+
seconds: Math.floor(diff % 60000 / 1000)
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Market SDK - ListingCard 组件
|
|
845
|
+
* 市场商品卡片
|
|
846
|
+
*/
|
|
847
|
+
|
|
848
|
+
function ListingCard({
|
|
849
|
+
listing,
|
|
850
|
+
onBuy,
|
|
851
|
+
onBid,
|
|
852
|
+
onClick,
|
|
853
|
+
showActions = true,
|
|
854
|
+
className = ''
|
|
855
|
+
}) {
|
|
856
|
+
const {
|
|
857
|
+
buy,
|
|
858
|
+
placeBid,
|
|
859
|
+
loading
|
|
860
|
+
} = useMarket();
|
|
861
|
+
const [timeRemaining, setTimeRemaining] = React.useState(null);
|
|
862
|
+
const {
|
|
863
|
+
item_name,
|
|
864
|
+
item_image,
|
|
865
|
+
listing_type,
|
|
866
|
+
status,
|
|
867
|
+
price,
|
|
868
|
+
highest_bid,
|
|
869
|
+
currency,
|
|
870
|
+
end_at,
|
|
871
|
+
bid_count
|
|
872
|
+
} = listing;
|
|
873
|
+
|
|
874
|
+
// 拍卖倒计时
|
|
875
|
+
React.useEffect(() => {
|
|
876
|
+
if (listing_type !== ListingType.AUCTION || !end_at) return;
|
|
877
|
+
const updateTime = () => {
|
|
878
|
+
setTimeRemaining(getTimeRemaining(end_at));
|
|
879
|
+
};
|
|
880
|
+
updateTime();
|
|
881
|
+
const timer = setInterval(updateTime, 1000);
|
|
882
|
+
return () => clearInterval(timer);
|
|
883
|
+
}, [listing_type, end_at]);
|
|
884
|
+
const handleBuy = async e => {
|
|
885
|
+
e.stopPropagation();
|
|
886
|
+
try {
|
|
887
|
+
const result = await buy(listing.id);
|
|
888
|
+
onBuy?.(result);
|
|
889
|
+
} catch (err) {
|
|
890
|
+
// 错误已在 context 处理
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
const handleBid = async e => {
|
|
894
|
+
e.stopPropagation();
|
|
895
|
+
onBid?.(listing);
|
|
896
|
+
};
|
|
897
|
+
const isAuction = listing_type === ListingType.AUCTION;
|
|
898
|
+
const ended = isAuction && isAuctionEnded(listing);
|
|
899
|
+
const displayPrice = isAuction ? highest_bid || price : price;
|
|
900
|
+
const formatTimeRemaining = () => {
|
|
901
|
+
if (!timeRemaining || timeRemaining.ended) return t('statusExpired');
|
|
902
|
+
const {
|
|
903
|
+
days,
|
|
904
|
+
hours,
|
|
905
|
+
minutes,
|
|
906
|
+
seconds
|
|
907
|
+
} = timeRemaining;
|
|
908
|
+
if (days > 0) return `${days}天 ${hours}时`;
|
|
909
|
+
if (hours > 0) return `${hours}时 ${minutes}分`;
|
|
910
|
+
return `${minutes}:${String(seconds).padStart(2, '0')}`;
|
|
911
|
+
};
|
|
912
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
913
|
+
className: `listing-card ${className}`,
|
|
914
|
+
onClick: () => onClick?.(listing)
|
|
915
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
916
|
+
className: "listing-image"
|
|
917
|
+
}, item_image ? /*#__PURE__*/React.createElement("img", {
|
|
918
|
+
src: item_image,
|
|
919
|
+
alt: item_name
|
|
920
|
+
}) : /*#__PURE__*/React.createElement("div", {
|
|
921
|
+
className: "image-placeholder"
|
|
922
|
+
}, "\uD83C\uDFF7\uFE0F"), status === ListingStatus.SOLD && /*#__PURE__*/React.createElement("span", {
|
|
923
|
+
className: "listing-badge sold"
|
|
924
|
+
}, t('statusSold')), isAuction && !ended && /*#__PURE__*/React.createElement("span", {
|
|
925
|
+
className: "listing-badge auction"
|
|
926
|
+
}, "\u23F1 ", formatTimeRemaining())), /*#__PURE__*/React.createElement("div", {
|
|
927
|
+
className: "listing-info"
|
|
928
|
+
}, /*#__PURE__*/React.createElement("h4", {
|
|
929
|
+
className: "listing-name"
|
|
930
|
+
}, item_name), /*#__PURE__*/React.createElement("div", {
|
|
931
|
+
className: "listing-price-row"
|
|
932
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
933
|
+
className: "price-label"
|
|
934
|
+
}, isAuction ? highest_bid ? t('highestBid') : t('startPrice') : t('price')), /*#__PURE__*/React.createElement("span", {
|
|
935
|
+
className: "price-value"
|
|
936
|
+
}, formatPrice(displayPrice, currency))), isAuction && bid_count > 0 && /*#__PURE__*/React.createElement("span", {
|
|
937
|
+
className: "bid-count"
|
|
938
|
+
}, bid_count, " ", t('bids'))), showActions && status === ListingStatus.ACTIVE && !ended && /*#__PURE__*/React.createElement("div", {
|
|
939
|
+
className: "listing-actions"
|
|
940
|
+
}, isAuction ? /*#__PURE__*/React.createElement("button", {
|
|
941
|
+
className: "listing-btn listing-btn-primary",
|
|
942
|
+
onClick: handleBid,
|
|
943
|
+
disabled: loading
|
|
944
|
+
}, t('placeBid')) : /*#__PURE__*/React.createElement("button", {
|
|
945
|
+
className: "listing-btn listing-btn-primary",
|
|
946
|
+
onClick: handleBuy,
|
|
947
|
+
disabled: loading
|
|
948
|
+
}, loading ? '...' : t('buyNow'))));
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const MARKET_ANCHOR_NAMESPACE = 'qbit_market';
|
|
952
|
+
function pickResourceId(resource) {
|
|
953
|
+
return resource?.id || resource?.listing_id || resource?.listingId || resource?.order_id || resource?.orderId || resource?.transaction_id || resource?.transactionId;
|
|
954
|
+
}
|
|
955
|
+
function pickContentHash(resource, options) {
|
|
956
|
+
return options.contentHash || resource?.content_hash || resource?.listing_hash || resource?.order_hash || resource?.transaction_hash || resource?.hash || null;
|
|
957
|
+
}
|
|
958
|
+
async function buildMarketAnchorMemo(resource, options = {}) {
|
|
959
|
+
const {
|
|
960
|
+
buildQBitAnchorMemo
|
|
961
|
+
} = await import('@quantabit/qbit-chain-sdk');
|
|
962
|
+
const resourceType = options.resourceType || 'listing';
|
|
963
|
+
const resourceId = options.resourceId || pickResourceId(resource);
|
|
964
|
+
const contentHash = pickContentHash(resource, options);
|
|
965
|
+
return buildQBitAnchorMemo({
|
|
966
|
+
action: options.action || `${resourceType}_anchor`,
|
|
967
|
+
subject: resourceId ? `${resourceType}:${resourceId}` : resourceType,
|
|
968
|
+
resource_id: resourceId,
|
|
969
|
+
content_hash: contentHash,
|
|
970
|
+
version: options.version,
|
|
971
|
+
timestamp: options.timestamp,
|
|
972
|
+
extra: {
|
|
973
|
+
item_type: resource?.item_type || resource?.itemType,
|
|
974
|
+
seller_did: resource?.seller_did,
|
|
975
|
+
buyer_did: resource?.buyer_did,
|
|
976
|
+
price: resource?.price,
|
|
977
|
+
...options.extra
|
|
978
|
+
}
|
|
979
|
+
}, {
|
|
980
|
+
namespace: options.namespace || MARKET_ANCHOR_NAMESPACE,
|
|
981
|
+
maxBytes: options.maxBytes
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
function buildMarketChainSubmit(resourceId, txHash, options = {}) {
|
|
985
|
+
return {
|
|
986
|
+
resource_id: resourceId,
|
|
987
|
+
tx_hash: txHash,
|
|
988
|
+
chain: options.chain || 'qbit',
|
|
989
|
+
network: options.network || 'mainnet',
|
|
990
|
+
action: options.action || 'market_anchor',
|
|
991
|
+
resource_type: options.resourceType || 'listing',
|
|
992
|
+
memo: options.memo,
|
|
993
|
+
content_hash: options.contentHash,
|
|
994
|
+
...options.extra
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* 将市场资源锚定到 QBit 链(构建 memo → 钱包签名广播 → 可选确认)
|
|
1000
|
+
*
|
|
1001
|
+
* @param {Object} provider - QBit 钱包 provider(signAndSendTransaction / signTransaction)
|
|
1002
|
+
* @param {Object} resource - 市场资源(listing/order/transaction)
|
|
1003
|
+
* @param {Object} options - 选项(endpoint, network, address, waitForConfirmation, priorityFee...)
|
|
1004
|
+
* @returns {Promise<{txHash: string|null, memo: string, confirmed?: boolean, slot?: number|null, submit: Object}>}
|
|
1005
|
+
*/
|
|
1006
|
+
async function anchorMarketResource(provider, resource, options = {}) {
|
|
1007
|
+
const sdk = await import('@quantabit/qbit-chain-sdk');
|
|
1008
|
+
const connection = new sdk.QBitConnection({
|
|
1009
|
+
endpoint: options.endpoint,
|
|
1010
|
+
network: options.network || 'mainnet'
|
|
1011
|
+
});
|
|
1012
|
+
const memo = await buildMarketAnchorMemo(resource, options);
|
|
1013
|
+
const result = await sdk.broadcastQBitAnchor(connection, provider, memo, {
|
|
1014
|
+
address: options.address,
|
|
1015
|
+
priorityFee: options.priorityFee,
|
|
1016
|
+
skipPreflight: options.skipPreflight,
|
|
1017
|
+
waitForConfirmation: options.waitForConfirmation,
|
|
1018
|
+
commitment: options.commitment,
|
|
1019
|
+
timeoutMs: options.confirmTimeoutMs
|
|
1020
|
+
});
|
|
1021
|
+
const resourceId = options.resourceId || pickResourceId(resource);
|
|
1022
|
+
return {
|
|
1023
|
+
...result,
|
|
1024
|
+
submit: buildMarketChainSubmit(resourceId, result.txHash, {
|
|
1025
|
+
...options,
|
|
1026
|
+
memo: result.memo,
|
|
1027
|
+
contentHash: pickContentHash(resource, options)
|
|
1028
|
+
})
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* 校验市场资源是否已成功锚定到链上
|
|
1034
|
+
*
|
|
1035
|
+
* @param {string} txHash - 交易签名
|
|
1036
|
+
* @param {Object} options - { endpoint, network, match }
|
|
1037
|
+
* @returns {Promise<{found: boolean, matched: boolean, memos: Array<Object>}>}
|
|
1038
|
+
*/
|
|
1039
|
+
async function verifyMarketAnchor(txHash, options = {}) {
|
|
1040
|
+
const sdk = await import('@quantabit/qbit-chain-sdk');
|
|
1041
|
+
const connection = new sdk.QBitConnection({
|
|
1042
|
+
endpoint: options.endpoint,
|
|
1043
|
+
network: options.network || 'mainnet'
|
|
1044
|
+
});
|
|
1045
|
+
return sdk.verifyAnchoredMemo(connection, txHash, {
|
|
1046
|
+
namespace: options.namespace || MARKET_ANCHOR_NAMESPACE,
|
|
1047
|
+
match: options.match,
|
|
1048
|
+
commitment: options.commitment
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
exports.BidStatus = BidStatus;
|
|
1053
|
+
exports.ItemType = ItemType;
|
|
1054
|
+
exports.ListingCard = ListingCard;
|
|
1055
|
+
exports.ListingStatus = ListingStatus;
|
|
1056
|
+
exports.ListingType = ListingType;
|
|
1057
|
+
exports.MARKET_ANCHOR_NAMESPACE = MARKET_ANCHOR_NAMESPACE;
|
|
1058
|
+
exports.MarketApiClient = MarketApiClient;
|
|
1059
|
+
exports.MarketProvider = MarketProvider;
|
|
1060
|
+
exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
|
|
1061
|
+
exports.anchorMarketResource = anchorMarketResource;
|
|
1062
|
+
exports.buildMarketAnchorMemo = buildMarketAnchorMemo;
|
|
1063
|
+
exports.buildMarketChainSubmit = buildMarketChainSubmit;
|
|
1064
|
+
exports.calculateTotalPrice = calculateTotalPrice;
|
|
1065
|
+
exports.createDefaultBid = createDefaultBid;
|
|
1066
|
+
exports.createDefaultListing = createDefaultListing;
|
|
1067
|
+
exports.formatPrice = formatPrice;
|
|
1068
|
+
exports.getLanguage = getLanguage;
|
|
1069
|
+
exports.getTimeRemaining = getTimeRemaining;
|
|
1070
|
+
exports.isAuctionEnded = isAuctionEnded;
|
|
1071
|
+
exports.marketApi = marketApi;
|
|
1072
|
+
exports.messages = messages;
|
|
1073
|
+
exports.setLanguage = setLanguage;
|
|
1074
|
+
exports.t = t;
|
|
1075
|
+
exports.useMarket = useMarket;
|
|
1076
|
+
exports.verifyMarketAnchor = verifyMarketAnchor;
|
|
1077
|
+
//# sourceMappingURL=index.cjs.map
|