punkkit-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.
Files changed (94) hide show
  1. package/.env +47 -0
  2. package/.gitmodules +3 -0
  3. package/README.md +158 -0
  4. package/config/auction.config.ts +40 -0
  5. package/config/env.config.ts +204 -0
  6. package/config/uniswap.config.ts +107 -0
  7. package/config/voucher.config.ts +10 -0
  8. package/contracts/MutiVoucher.sol +78 -0
  9. package/contracts/auction/ChainXAuction.sol +177 -0
  10. package/contracts/auction/ChainXAuctionV2.sol +672 -0
  11. package/contracts/auction/ChainXWrappedETH.sol +80 -0
  12. package/contracts/auction/ChainYLiquidityManager.sol +57 -0
  13. package/contracts/auction/ChainYShadowETH.sol +148 -0
  14. package/contracts/auction/ChainYVault.sol +195 -0
  15. package/contracts/auction/ChainYVaultCoinbase.sol +276 -0
  16. package/contracts/auction/ChainYVaultV2.sol +318 -0
  17. package/contracts/auction/coinbase-and-stake/README.md +55 -0
  18. package/contracts/auction/coinbase-and-stake/coinbase.sol +142 -0
  19. package/contracts/auction/coinbase-and-stake/invokeCoinbase.sol +159 -0
  20. package/contracts/auction/coinbase-and-stake/invokeStake.sol +82 -0
  21. package/contracts/auction/coinbase-and-stake/stake.sol +92 -0
  22. package/contracts/auction/interfaces/IUniswapV2Factory.sol +15 -0
  23. package/contracts/auction/interfaces/IUniswapV2Pair.sol +53 -0
  24. package/contracts/auction/interfaces/IUniswapV2Router02.sol +25 -0
  25. package/contracts/auction/interfaces/IUnlockStrategy.sol +18 -0
  26. package/contracts/auction/libraries/EventParser.sol +32 -0
  27. package/contracts/auction/libraries/TransactionParser.sol +70 -0
  28. package/contracts/auction/strategies/MatchResultWithdrawnStrategy.sol +33 -0
  29. package/contracts/auction/utils/BytesLib.sol +180 -0
  30. package/contracts/auction/utils/RLPReader.sol +355 -0
  31. package/contracts/uniswap/Create2.sol +80 -0
  32. package/contracts/uniswap/DynamicFee.sol +100 -0
  33. package/contracts/uniswap/Example.sol +35 -0
  34. package/contracts/uniswap/HookMiner.sol +52 -0
  35. package/contracts/uniswap/LimitOrder.sol +486 -0
  36. package/contracts/uniswap/LiquidPool.sol +179 -0
  37. package/contracts/uniswap/MockERC20.sol +20 -0
  38. package/hardhat.config.ts +35 -0
  39. package/ignition/modules/LimitOrder.ts +33 -0
  40. package/package.json +32 -0
  41. package/scripts/auction/deploy.ts +23 -0
  42. package/scripts/auction/deployCoinbase.ts +21 -0
  43. package/scripts/auction/deployXAuction.ts +23 -0
  44. package/scripts/auction/deployYVault.ts +22 -0
  45. package/scripts/deploy_voucher.ts +20 -0
  46. package/scripts/uniswap/deploy/deploy.ts +65 -0
  47. package/scripts/uniswap/deploy/deploy_create2.ts +11 -0
  48. package/scripts/uniswap/deploy/deploy_example.ts +35 -0
  49. package/scripts/uniswap/deploy/deploy_hooks.ts +74 -0
  50. package/scripts/uniswap/deploy/deploy_mockERC20.ts +42 -0
  51. package/scripts/uniswap/deploy/help.ts +96 -0
  52. package/scripts/uniswap/deploy/init.ts +70 -0
  53. package/src/auction/chainXAuction.ts +209 -0
  54. package/src/auction/chainYVault.ts +153 -0
  55. package/src/auction/event.ts +19 -0
  56. package/src/auction/serialize.ts +162 -0
  57. package/src/auction/type.ts +71 -0
  58. package/src/lib/signer.ts +20 -0
  59. package/src/lib/unlock.ts +14 -0
  60. package/src/uniswap/1-marketprice/addLiquidity.ts +80 -0
  61. package/src/uniswap/1-marketprice/removeLiquidity.ts +63 -0
  62. package/src/uniswap/1-marketprice/swap.ts +100 -0
  63. package/src/uniswap/2-limitorder/kill.ts +70 -0
  64. package/src/uniswap/2-limitorder/place.ts +93 -0
  65. package/src/uniswap/2-limitorder/withdraw.ts +78 -0
  66. package/src/uniswap/3-dynamicfee/dynamicfee.ts +321 -0
  67. package/src/uniswap/lib/ERC20.ts +49 -0
  68. package/src/uniswap/lib/contract.ts +18 -0
  69. package/src/uniswap/lib/limitOrder.ts +40 -0
  70. package/src/uniswap/lib/liqCalculation.ts +152 -0
  71. package/src/uniswap/lib/listen.ts +57 -0
  72. package/src/uniswap/lib/pool.ts +62 -0
  73. package/src/uniswap/lib/swap.ts +8 -0
  74. package/src/uniswap/lib/types.ts +21 -0
  75. package/src/uniswap/lib/utils.ts +26 -0
  76. package/src/uniswap/playgroud/abiencode.ts +21 -0
  77. package/src/uniswap/playgroud/amount0.ts +47 -0
  78. package/src/uniswap/playgroud/errordecode.ts +54 -0
  79. package/src/uniswap/playgroud/errorsigs.ts +86 -0
  80. package/src/voucher.ts +122 -0
  81. package/test/auction/ChainXAuctionV2.test.ts +265 -0
  82. package/test/auction/ChainYVaultV2.test.js +163 -0
  83. package/test/auction/ChainYVaultV2.test.ts +183 -0
  84. package/test/auction/auction.test.ts +106 -0
  85. package/test/connect_punk.test.ts +26 -0
  86. package/test/create2.test.ts +44 -0
  87. package/test/normal.ts +43 -0
  88. package/test/test-config.ts +18 -0
  89. package/test/uniswap/example.test.ts +62 -0
  90. package/test/uniswap/limitOrder.test.ts +184 -0
  91. package/test/uniswap/mockERC20.test.ts +142 -0
  92. package/test/voucher_hardhat.test.ts +120 -0
  93. package/test/voucher_punk.test.ts +83 -0
  94. package/tsconfig.json +11 -0
@@ -0,0 +1,672 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
5
+ import "@openzeppelin/contracts/access/Ownable.sol";
6
+ import "./libraries/TransactionParser.sol";
7
+ import "./libraries/EventParser.sol";
8
+ import "hardhat/console.sol";
9
+
10
+ contract ChainXAuctionV2 is ReentrancyGuard, Ownable {
11
+ // ============ 状态变量 ============
12
+ address public vaultAddress;
13
+ mapping(uint256 => Auction) public auctions;
14
+
15
+ // Auction Start: Auction | REVEAL_PERIOD_SECRECT | REVEAL_PERIOD_BID | CHALLENGE_PERIOD | WITHDRAW_PERIOD | End
16
+ uint256 public constant CHALLENGE_PERIOD = 1 hours;
17
+ uint256 public constant REVEAL_PERIOD_SECRECT = 1 hours;
18
+ uint256 public constant REVEAL_PERIOD_BID = 1 hours;
19
+ uint256 public constant WITHDRAW_PERIOD = 1 hours;
20
+
21
+ // ============ 结构体定义 ============
22
+ struct Vault {
23
+ bool exists;
24
+ uint256 revealTime; // 实际的揭示时间,并代表isRevealed(e.g. lockInfo.revealTime == 0)
25
+ bytes32 vaultSecretHash;
26
+ uint256 value;
27
+ bytes32 salt;
28
+ }
29
+
30
+ struct Bid {
31
+ bytes32 bidSecretHash;
32
+ uint256 bidValue;
33
+ bool isSealed;
34
+ bytes32[] targetVaults; // 一个bid可以对应多个vault,即竞标多个资产
35
+ uint256 revealTime;
36
+ uint256 depositAmount;
37
+ }
38
+
39
+ struct Auction {
40
+ uint256 chainid;
41
+ uint32 auctionType; // 竞标类型
42
+ uint256 revealTime;
43
+ bool isActive; // 是否激活
44
+ mapping(bytes32 => Vault) vaults;
45
+ uint256 vaultCount;
46
+ mapping(address => Bid) auctionBid;
47
+ uint256 bidCount;
48
+ mapping(bytes32 => address) vaultToBidder;
49
+ mapping(address => MatchResult) auctionMatches; // 修改: 从 lockId 改为 bidder 作为 key
50
+ uint256 challengeEndTime; // 挑战期结束时间
51
+ mapping(address => mapping(bytes32 => bool)) sellerLocks; // 新增: seller -> lockId -> exists
52
+ }
53
+
54
+ // 跨链数据,包含原始交易和收据
55
+ struct CrossChainData {
56
+ uint256 sourceChainId;
57
+ bytes rawTransaction;
58
+ bytes rawRecpt;
59
+ }
60
+
61
+ struct MatchResult {
62
+ bytes32 vaultId; // 新增: 记录对应的 lockId
63
+ uint256 bidValue; // 新增: 记录中标金额
64
+ bool isChallenged;
65
+ bool isWithdrawn;
66
+ }
67
+
68
+ // ============ 事件定义 ============
69
+ event AuctionCreated(
70
+ uint256 indexed auctionId,
71
+ uint32 auctionType,
72
+ uint256 endTime
73
+ );
74
+ event LockRevealed(
75
+ uint256 indexed auctionId,
76
+ bytes32 indexed lockId,
77
+ uint256 revealTime
78
+ );
79
+ event BidPlaced(
80
+ uint256 indexed auctionId,
81
+ bytes32 indexed lockId,
82
+ address indexed bidder,
83
+ uint256 depositAmount
84
+ );
85
+ event BidRevealed(
86
+ uint256 indexed auctionId,
87
+ address indexed bidder,
88
+ uint256 bidRevealValue
89
+ );
90
+ event MatchResultSubmitted(
91
+ uint256 indexed auctionId,
92
+ bytes32 indexed lockId,
93
+ address indexed bidder,
94
+ uint256 bidValue
95
+ );
96
+ event MatchResultChallenged(
97
+ uint256 indexed auctionId,
98
+ bytes32 indexed lockId,
99
+ address indexed challenger,
100
+ string reason
101
+ );
102
+ event MatchResultWithdrawn(
103
+ uint256 indexed auctionId,
104
+ bytes32 indexed lockId,
105
+ address indexed recipient,
106
+ uint256 amount
107
+ );
108
+ event LockCreated(
109
+ uint256 indexed auctionId,
110
+ bytes32 indexed lockId,
111
+ uint256 revealTime
112
+ );
113
+ event BidUpdated(
114
+ uint256 indexed auctionId,
115
+ bytes32 indexed lockId,
116
+ address indexed bidder
117
+ );
118
+ event VaultAddressUpdated(
119
+ address indexed oldVault,
120
+ address indexed newVault
121
+ );
122
+
123
+ // ============ 拍卖核心功能 ============
124
+ constructor(
125
+ address initialVaultAddress
126
+ ) ReentrancyGuard() Ownable(msg.sender) {
127
+ require(
128
+ initialVaultAddress != address(0),
129
+ "Invalid initial vault address"
130
+ );
131
+ vaultAddress = initialVaultAddress;
132
+ emit VaultAddressUpdated(address(0), initialVaultAddress);
133
+ }
134
+
135
+ function setVaultAddress(address newVaultAddress) external onlyOwner {
136
+ require(newVaultAddress != address(0), "Invalid vault address");
137
+ address oldVault = vaultAddress;
138
+ vaultAddress = newVaultAddress;
139
+ emit VaultAddressUpdated(oldVault, newVaultAddress);
140
+ }
141
+
142
+ // 盘古上部署 X
143
+ // Y 拍卖 auction,
144
+ // X 上同步启动,X开始竞价 ETH
145
+ function createAuction(bytes memory crossChainMessage) external {
146
+ // 无需验证,直接反向传输
147
+ CrossChainData memory ccData = abi.decode(
148
+ crossChainMessage,
149
+ (CrossChainData)
150
+ );
151
+
152
+ // 解析原始交易
153
+ TransactionParser.RawTransaction memory rawTx = TransactionParser
154
+ .parseRawTransaction(ccData.rawTransaction);
155
+ TransactionParser.RecptLog memory log = TransactionParser.parseRecptLog(
156
+ ccData.rawRecpt
157
+ );
158
+
159
+ // 解析拍卖参数
160
+ uint256 auctionId = uint256(log.topics[1]); // auctionID 是 indexed修饰因此再topic中。
161
+ uint32 auctionType;
162
+ uint256 activeAuctionCount;
163
+ uint256 revealStartTime;
164
+
165
+ (auctionType, activeAuctionCount, revealStartTime) = abi.decode(
166
+ log.eventData,
167
+ (uint32, uint256, uint256)
168
+ );
169
+ require(auctionId != 0, "Invalid auction, auctionID=0");
170
+ console.log("activeAuctionCount:", activeAuctionCount);
171
+
172
+ // ! 版本1:解析拍卖参数
173
+ // (uint256 auctionId, uint32 auctionType, uint256 nonce, uint256 revealStartTime) = EventParser.parseAuctionCreatedEvent(log.eventData);
174
+ // 校验auctionId与message sender的绑定关系
175
+ // 恶意sender无法伪造其他sender的auctionId
176
+ // 恶意sender无法在其他链重放自己的auction
177
+ address sender = signerRecover(rawTx);
178
+
179
+ uint256 auctionCheckId = uint256(
180
+ keccak256(
181
+ abi.encodePacked(
182
+ sender,
183
+ ccData.sourceChainId,
184
+ vaultAddress,
185
+ activeAuctionCount
186
+ )
187
+ )
188
+ );
189
+
190
+ console.log("auctionCheckId:", auctionCheckId);
191
+ console.log("auctionId:", auctionId);
192
+
193
+ require(auctionCheckId == auctionId, "Invalid auctionId");
194
+
195
+ auctions[auctionId].chainid = ccData.sourceChainId;
196
+ auctions[auctionId].auctionType = auctionType;
197
+ auctions[auctionId].revealTime =
198
+ revealStartTime +
199
+ REVEAL_PERIOD_SECRECT;
200
+ auctions[auctionId].isActive = true;
201
+ auctions[auctionId].challengeEndTime =
202
+ revealStartTime +
203
+ REVEAL_PERIOD_SECRECT +
204
+ REVEAL_PERIOD_BID +
205
+ CHALLENGE_PERIOD;
206
+
207
+ emit AuctionCreated(
208
+ auctionId,
209
+ auctionType,
210
+ auctions[auctionId].revealTime
211
+ );
212
+ }
213
+
214
+ function signerRecover(TransactionParser.RawTransaction memory rawTx) internal pure returns (address sender)
215
+ {
216
+ uint8 vAdj = uint8(rawTx.v);
217
+ if (vAdj >= 35) {
218
+ vAdj = 27 + uint8((vAdj - 35) % 2); // normalize EIP-155 v to 27/28
219
+ }
220
+ uint256 chainIdSig = 0;
221
+ if (rawTx.v >= 35) {
222
+ chainIdSig = (uint256(rawTx.v) - 35) / 2;
223
+ }
224
+
225
+ bytes[] memory payload = new bytes[](9);
226
+ payload[0] = _encodeUint(rawTx.nonce);
227
+ payload[1] = _encodeUint(rawTx.gasPrice);
228
+ payload[2] = _encodeUint(rawTx.gasLimit);
229
+ payload[3] = _encodeAddress(rawTx.to);
230
+ payload[4] = _encodeUint(rawTx.value);
231
+ payload[5] = _encodeBytes(rawTx.data);
232
+ payload[6] = _encodeUint(chainIdSig);
233
+ payload[7] = _encodeUint(0);
234
+ payload[8] = _encodeUint(0);
235
+
236
+ bytes32 txHash = keccak256(_encodeList(payload));
237
+ sender = ecrecover(txHash, vAdj, rawTx.r, rawTx.s);
238
+ console.logBytes32(txHash);
239
+ console.log("vAdj:", vAdj);
240
+ console.log("chainID:",chainIdSig);
241
+ console.log("sender address:", sender);
242
+ }
243
+
244
+ // 调试接口:根据 rawTx 计算恢复的签名者
245
+ function debugRecover(
246
+ bytes memory rawTransaction
247
+ )
248
+ external
249
+ pure
250
+ returns (
251
+ address signer,
252
+ bytes32 txHash,
253
+ uint256 chainIdSig,
254
+ uint8 vAdj,
255
+ uint256 nonce,
256
+ uint256 gasPrice,
257
+ uint256 gasLimit,
258
+ uint256 value
259
+ )
260
+ {
261
+ TransactionParser.RawTransaction memory rawTx = TransactionParser
262
+ .parseRawTransaction(rawTransaction);
263
+
264
+ vAdj = uint8(rawTx.v);
265
+ if (vAdj >= 35) {
266
+ vAdj = 27 + uint8((vAdj - 35) % 2);
267
+ chainIdSig = (uint256(rawTx.v) - 35) / 2;
268
+ }
269
+
270
+ nonce = rawTx.nonce;
271
+ gasPrice = rawTx.gasPrice;
272
+ gasLimit = rawTx.gasLimit;
273
+ value = rawTx.value;
274
+
275
+ bytes[] memory payload = new bytes[](9);
276
+ payload[0] = _encodeUint(rawTx.nonce);
277
+ payload[1] = _encodeUint(rawTx.gasPrice);
278
+ payload[2] = _encodeUint(rawTx.gasLimit);
279
+ payload[3] = _encodeAddress(rawTx.to);
280
+ payload[4] = _encodeUint(rawTx.value);
281
+ payload[5] = _encodeBytes(rawTx.data);
282
+ payload[6] = _encodeUint(chainIdSig);
283
+ payload[7] = _encodeUint(0);
284
+ payload[8] = _encodeUint(0);
285
+
286
+ txHash = keccak256(_encodeList(payload));
287
+ signer = ecrecover(txHash, vAdj, rawTx.r, rawTx.s);
288
+ }
289
+
290
+ function placeBid(
291
+ uint256 auctionId,
292
+ address seller,
293
+ bytes32 vaultSecretHash,
294
+ bytes32 bidSecretHash
295
+ ) external payable {
296
+ Auction storage auction = auctions[auctionId];
297
+ require(auction.isActive, "Auction not active");
298
+ require(block.timestamp < auction.revealTime, "Auction ended");
299
+
300
+ // auction可以支持多个vault竞标,每个vault对应一个lockId
301
+ bytes32 targetLockId = keccak256(
302
+ abi.encodePacked(
303
+ auction.chainid,
304
+ vaultAddress,
305
+ auctionId,
306
+ seller,
307
+ vaultSecretHash,
308
+ auction.auctionType
309
+ )
310
+ );
311
+
312
+ if (!auction.vaults[targetLockId].exists) {
313
+ Vault storage newLock = auction.vaults[targetLockId];
314
+ newLock.exists = true;
315
+ newLock.vaultSecretHash = vaultSecretHash;
316
+ auction.vaultCount++;
317
+ emit LockCreated(auctionId, targetLockId, auction.revealTime);
318
+ }
319
+
320
+ if (auction.auctionBid[msg.sender].revealTime == 0) {
321
+ console.log("New bid from bidder:", msg.sender);
322
+ Bid storage newBid = auction.auctionBid[msg.sender];
323
+ newBid.bidSecretHash = bidSecretHash;
324
+ newBid.isSealed = isSealedAuction(auction.auctionType);
325
+ newBid.targetVaults.push(targetLockId);
326
+ newBid.revealTime =
327
+ auction.revealTime +
328
+ REVEAL_PERIOD_SECRECT +
329
+ REVEAL_PERIOD_BID;
330
+ newBid.depositAmount = msg.value; // Bid出价时质押的值,需要比真实出价值高,bid结束后,可以通过claim提取未使用部分
331
+
332
+ console.log("Deposit amount:", msg.value);
333
+ emit BidPlaced(auctionId, targetLockId, msg.sender, msg.value);
334
+ } else {
335
+ // 已经出价bid,但是可以追加竞标多个vault
336
+ console.log("Old bid from bidder:", msg.sender);
337
+ Bid storage existingBid = auction.auctionBid[msg.sender];
338
+ existingBid.targetVaults.push(targetLockId);
339
+ emit BidUpdated(auctionId, targetLockId, msg.sender);
340
+ }
341
+ }
342
+
343
+ // 卖方揭示Lock信息,通过公开value和salt证明其secretHash的正确性。
344
+ function revealLock(
345
+ uint256 auctionId,
346
+ bytes32 lockId,
347
+ uint256 value,
348
+ bytes32 salt
349
+ ) external {
350
+ Auction storage auction = auctions[auctionId];
351
+ require(auction.isActive, "Auction not active");
352
+ require(block.timestamp < auction.revealTime, "Auction ended");
353
+
354
+ Vault storage lockInfo = auction.vaults[lockId];
355
+ require(lockInfo.exists, "Lock does not exist");
356
+ require(lockInfo.revealTime == 0, "Lock already revealed");
357
+ require(
358
+ keccak256(abi.encodePacked(value, salt)) ==
359
+ lockInfo.vaultSecretHash,
360
+ "Invalid lock reveal, hash secret is not match"
361
+ );
362
+
363
+ lockInfo.revealTime = block.timestamp;
364
+ lockInfo.value = value;
365
+ lockInfo.salt = salt;
366
+ auction.vaultCount++;
367
+
368
+ emit LockRevealed(auctionId, lockId, block.timestamp);
369
+ }
370
+
371
+ // 竞标者揭示bid信息
372
+ function revealBid(
373
+ uint256 auctionId,
374
+ uint256 value,
375
+ bytes32 salt
376
+ ) external {
377
+ Auction storage auction = auctions[auctionId];
378
+ require(auction.isActive, "Auction not active");
379
+ Bid storage bid = auction.auctionBid[msg.sender];
380
+ require(bid.bidValue == 0, "Bid already revealed");
381
+
382
+ if (bid.isSealed) {
383
+ // 密封竞标,需要验证hash
384
+ require(block.timestamp < bid.revealTime, "Bid reveal time ended");
385
+ require(
386
+ keccak256(abi.encodePacked(value, salt)) == bid.bidSecretHash,
387
+ "Invalid bid reveal"
388
+ );
389
+ }
390
+ bid.bidValue = value;
391
+ bid.revealTime = block.timestamp;
392
+ auction.bidCount++;
393
+ emit BidRevealed(auctionId, msg.sender, bid.bidValue);
394
+ }
395
+
396
+ // 卖家未揭示,出价方可提取押金
397
+ function claimBidDepositAfterReveal(uint256 auctionId) external {
398
+ Auction storage auction = auctions[auctionId];
399
+ require(
400
+ block.timestamp > auction.revealTime,
401
+ "Reveal period not ended"
402
+ );
403
+ require(auction.auctionType == 0, "Auction is not a sealed auction");
404
+
405
+ // bid.bidLocks所有对应的locks均未reveal
406
+ Bid storage bid = auction.auctionBid[msg.sender];
407
+ for (uint256 i = 0; i < bid.targetVaults.length; i++) {
408
+ bytes32 vaultId = bid.targetVaults[i];
409
+ require(
410
+ auction.vaults[vaultId].salt != bytes32(0),
411
+ "Vault is not revealed"
412
+ );
413
+ }
414
+
415
+ uint256 depositAmount = bid.depositAmount;
416
+ bid.depositAmount = 0;
417
+ payable(msg.sender).transfer(depositAmount);
418
+ }
419
+
420
+ function claimBidDepositAfterChallenge(uint256 auctionId) external {
421
+ Auction storage auction = auctions[auctionId];
422
+ require(
423
+ block.timestamp > auction.challengeEndTime,
424
+ "Challenge period not ended"
425
+ );
426
+ // 判断bid可提取,未竞价成功
427
+ require(
428
+ auction.auctionMatches[msg.sender].isChallenged ||
429
+ auction.auctionMatches[msg.sender].vaultId == bytes32(0),
430
+ "Bid is challenged or not win the auction"
431
+ );
432
+
433
+ Bid storage bid = auction.auctionBid[msg.sender];
434
+ uint256 depositAmount = bid.depositAmount;
435
+ bid.depositAmount = 0;
436
+ payable(msg.sender).transfer(depositAmount);
437
+ }
438
+
439
+ function claimBidDepositAfterWithdraw(uint256 auctionId) external {
440
+ Auction storage auction = auctions[auctionId];
441
+ require(
442
+ block.timestamp > auction.challengeEndTime + WITHDRAW_PERIOD,
443
+ "Withdraw period not ended"
444
+ );
445
+
446
+ Bid storage bid = auction.auctionBid[msg.sender];
447
+ uint256 depositAmount = bid.depositAmount;
448
+ bid.depositAmount = 0;
449
+ payable(msg.sender).transfer(depositAmount);
450
+ }
451
+
452
+ // 缺少步骤:竞标成功的判断在链上不好做?如果是简单的价高者可以实现,但是复杂逻辑链上实现复杂。
453
+ // 如果要实现竞标成功判断? 挑战机制,链外计算
454
+ // submitMatchResults 只负责拍卖结果上传(已经确定好中标人以后的操作)
455
+
456
+ // ============ 匹配结果管理 ============
457
+ function submitMatchResults(
458
+ uint256 auctionId,
459
+ bytes32[] calldata lockIds,
460
+ address[] calldata bidders,
461
+ uint256[] calldata finalValues
462
+ ) external {
463
+ require(
464
+ lockIds.length == bidders.length &&
465
+ bidders.length == finalValues.length,
466
+ "Length mismatch"
467
+ );
468
+
469
+ Auction storage auction = auctions[auctionId];
470
+ require(lockIds.length == auction.vaultCount, "Count mismatch");
471
+ require(auction.isActive, "Auction not active");
472
+ require(
473
+ block.timestamp >= auction.revealTime + REVEAL_PERIOD_BID,
474
+ "Bid reveal period not ended"
475
+ );
476
+
477
+ for (uint256 i = 0; i < lockIds.length; i++) {
478
+ bytes32 lockId = lockIds[i];
479
+ address bidder = bidders[i];
480
+ uint256 finalValue = finalValues[i];
481
+ require(auction.vaults[lockId].exists, "Invalid lock ID");
482
+ require(
483
+ auction.vaults[lockId].revealTime == 0,
484
+ "Lock not revealed"
485
+ );
486
+
487
+ auction.auctionMatches[bidder] = MatchResult({
488
+ vaultId: lockId,
489
+ isChallenged: false,
490
+ isWithdrawn: false,
491
+ bidValue: finalValue
492
+ });
493
+
494
+ auction.vaultToBidder[lockId] = bidder;
495
+
496
+ emit MatchResultSubmitted(auctionId, lockId, bidder, finalValue);
497
+ }
498
+ }
499
+
500
+ function challengeMatchResult(uint256 auctionId, address bidder) external {
501
+ Auction storage auction = auctions[auctionId];
502
+ require(auction.isActive, "Auction not active");
503
+ require(
504
+ block.timestamp < auction.challengeEndTime,
505
+ "Challenge period ended"
506
+ );
507
+
508
+ MatchResult storage matching = auction.auctionMatches[bidder];
509
+ require(!matching.isChallenged, "Already challenged");
510
+
511
+ // todo: 如何进行挑战?
512
+
513
+ matching.isChallenged = true;
514
+ emit MatchResultChallenged(auctionId, matching.vaultId, msg.sender, "");
515
+ }
516
+
517
+ // 提现方法,挑战期结束以后执行。 lockID来源于创建Auction时候的秘密值,需要通过ChainY的提款日志获取
518
+ // 通过withdrawMatchResult来触发chainYVaultV2的unlock
519
+ function withdrawMatchResult(
520
+ uint256 auctionId,
521
+ bytes32 vaultId
522
+ ) external nonReentrant {
523
+ Auction storage auction = auctions[auctionId];
524
+ require(auction.isActive, "Auction not active");
525
+ require(
526
+ block.timestamp >= auction.challengeEndTime,
527
+ "Challenge period not ended"
528
+ );
529
+
530
+ address bidder = auction.vaultToBidder[vaultId];
531
+ uint256 transferAmount = 0;
532
+ Vault storage vaultInfo = auction.vaults[vaultId];
533
+ require(vaultInfo.exists, "Vault does not exist");
534
+
535
+ if (vaultInfo.revealTime == 0) {
536
+ // 没有揭示,返还给vault
537
+ bidder = vaultAddress;
538
+ } else if (bidder != address(0)) {
539
+ MatchResult storage matching = auction.auctionMatches[bidder];
540
+ require(!matching.isChallenged, "Match was challenged");
541
+ require(!matching.isWithdrawn, "Already withdrawn");
542
+
543
+ Bid storage bid = auction.auctionBid[bidder];
544
+ bid.depositAmount -= transferAmount;
545
+ require(bid.depositAmount >= 0, "Deposit amount is negative");
546
+
547
+ transferAmount = matching.bidValue;
548
+ payable(msg.sender).transfer(transferAmount);
549
+ matching.isWithdrawn = true;
550
+ }
551
+ // else {} bidder = address(0)
552
+
553
+ emit MatchResultWithdrawn(auctionId, vaultId, bidder, transferAmount);
554
+ }
555
+
556
+ // ============ 查询功能 ============
557
+ function getLockInfo(
558
+ uint256 auctionId,
559
+ bytes32 lockId
560
+ ) external view returns (bool exists, uint256 revealTime, bytes32 secret) {
561
+ Vault memory lockInfo = auctions[auctionId].vaults[lockId];
562
+ return (
563
+ lockInfo.exists,
564
+ lockInfo.revealTime,
565
+ keccak256(abi.encodePacked(lockInfo.value, lockInfo.salt))
566
+ );
567
+ }
568
+
569
+ function getMatchResult(
570
+ uint256 auctionId,
571
+ address bidder
572
+ )
573
+ external
574
+ view
575
+ returns (
576
+ bytes32 lockId, // 修改返回值顺序
577
+ bool isChallenged,
578
+ bool isWithdrawn
579
+ )
580
+ {
581
+ MatchResult storage matching = auctions[auctionId].auctionMatches[
582
+ bidder
583
+ ];
584
+ return (matching.vaultId, matching.isChallenged, matching.isWithdrawn);
585
+ }
586
+
587
+ // ============ 内部功能 ============
588
+ // 是否为秘密竞拍
589
+ function isSealedAuction(uint32 auctionType) internal pure returns (bool) {
590
+ return (auctionType & 0x000000FF) == 0x000000FF;
591
+ }
592
+
593
+ // ============ RLP helpers (minimal encode) ============
594
+ function _encodeUint(uint256 value) internal pure returns (bytes memory) {
595
+ if (value == 0) {
596
+ return hex"80"; // empty string per RLP for zero
597
+ }
598
+ bytes memory b = _toMinimalBytes(value);
599
+ if (b.length == 1 && uint8(b[0]) < 0x80) {
600
+ return b; // single byte, no length prefix
601
+ }
602
+ return abi.encodePacked(bytes1(uint8(0x80 + b.length)), b);
603
+ }
604
+
605
+ function _encodeAddress(address addr) internal pure returns (bytes memory) {
606
+ bytes memory b = abi.encodePacked(addr);
607
+ return abi.encodePacked(bytes1(uint8(0x80 + b.length)), b);
608
+ }
609
+
610
+ function _encodeBytes(
611
+ bytes memory data
612
+ ) internal pure returns (bytes memory) {
613
+ if (data.length == 1 && uint8(data[0]) < 0x80) {
614
+ return data;
615
+ }
616
+ if (data.length <= 55) {
617
+ return abi.encodePacked(bytes1(uint8(0x80 + data.length)), data);
618
+ }
619
+ bytes memory lenBytes = _toMinimalBytes(data.length);
620
+ return
621
+ abi.encodePacked(
622
+ bytes1(uint8(0xb7 + lenBytes.length)),
623
+ lenBytes,
624
+ data
625
+ );
626
+ }
627
+
628
+ function _encodeList(
629
+ bytes[] memory list
630
+ ) internal pure returns (bytes memory) {
631
+ uint256 totalLen;
632
+ for (uint256 i = 0; i < list.length; i++) {
633
+ totalLen += list[i].length;
634
+ }
635
+
636
+ bytes memory payload;
637
+ if (totalLen <= 55) {
638
+ payload = abi.encodePacked(bytes1(uint8(0xc0 + totalLen)));
639
+ } else {
640
+ bytes memory lenBytes = _toMinimalBytes(totalLen);
641
+ payload = abi.encodePacked(
642
+ bytes1(uint8(0xf7 + lenBytes.length)),
643
+ lenBytes
644
+ );
645
+ }
646
+
647
+ for (uint256 i = 0; i < list.length; i++) {
648
+ payload = abi.encodePacked(payload, list[i]);
649
+ }
650
+ return payload;
651
+ }
652
+
653
+ function _toMinimalBytes(
654
+ uint256 value
655
+ ) internal pure returns (bytes memory) {
656
+ if (value == 0) {
657
+ return hex"00";
658
+ }
659
+ uint256 temp = value;
660
+ uint256 len;
661
+ while (temp != 0) {
662
+ len++;
663
+ temp >>= 8;
664
+ }
665
+ bytes memory out = new bytes(len);
666
+ for (uint256 i = len; i > 0; i--) {
667
+ out[i - 1] = bytes1(uint8(value));
668
+ value >>= 8;
669
+ }
670
+ return out;
671
+ }
672
+ }