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.
- package/.env +47 -0
- package/.gitmodules +3 -0
- package/README.md +158 -0
- package/config/auction.config.ts +40 -0
- package/config/env.config.ts +204 -0
- package/config/uniswap.config.ts +107 -0
- package/config/voucher.config.ts +10 -0
- package/contracts/MutiVoucher.sol +78 -0
- package/contracts/auction/ChainXAuction.sol +177 -0
- package/contracts/auction/ChainXAuctionV2.sol +672 -0
- package/contracts/auction/ChainXWrappedETH.sol +80 -0
- package/contracts/auction/ChainYLiquidityManager.sol +57 -0
- package/contracts/auction/ChainYShadowETH.sol +148 -0
- package/contracts/auction/ChainYVault.sol +195 -0
- package/contracts/auction/ChainYVaultCoinbase.sol +276 -0
- package/contracts/auction/ChainYVaultV2.sol +318 -0
- package/contracts/auction/coinbase-and-stake/README.md +55 -0
- package/contracts/auction/coinbase-and-stake/coinbase.sol +142 -0
- package/contracts/auction/coinbase-and-stake/invokeCoinbase.sol +159 -0
- package/contracts/auction/coinbase-and-stake/invokeStake.sol +82 -0
- package/contracts/auction/coinbase-and-stake/stake.sol +92 -0
- package/contracts/auction/interfaces/IUniswapV2Factory.sol +15 -0
- package/contracts/auction/interfaces/IUniswapV2Pair.sol +53 -0
- package/contracts/auction/interfaces/IUniswapV2Router02.sol +25 -0
- package/contracts/auction/interfaces/IUnlockStrategy.sol +18 -0
- package/contracts/auction/libraries/EventParser.sol +32 -0
- package/contracts/auction/libraries/TransactionParser.sol +70 -0
- package/contracts/auction/strategies/MatchResultWithdrawnStrategy.sol +33 -0
- package/contracts/auction/utils/BytesLib.sol +180 -0
- package/contracts/auction/utils/RLPReader.sol +355 -0
- package/contracts/uniswap/Create2.sol +80 -0
- package/contracts/uniswap/DynamicFee.sol +100 -0
- package/contracts/uniswap/Example.sol +35 -0
- package/contracts/uniswap/HookMiner.sol +52 -0
- package/contracts/uniswap/LimitOrder.sol +486 -0
- package/contracts/uniswap/LiquidPool.sol +179 -0
- package/contracts/uniswap/MockERC20.sol +20 -0
- package/hardhat.config.ts +35 -0
- package/ignition/modules/LimitOrder.ts +33 -0
- package/package.json +32 -0
- package/scripts/auction/deploy.ts +23 -0
- package/scripts/auction/deployCoinbase.ts +21 -0
- package/scripts/auction/deployXAuction.ts +23 -0
- package/scripts/auction/deployYVault.ts +22 -0
- package/scripts/deploy_voucher.ts +20 -0
- package/scripts/uniswap/deploy/deploy.ts +65 -0
- package/scripts/uniswap/deploy/deploy_create2.ts +11 -0
- package/scripts/uniswap/deploy/deploy_example.ts +35 -0
- package/scripts/uniswap/deploy/deploy_hooks.ts +74 -0
- package/scripts/uniswap/deploy/deploy_mockERC20.ts +42 -0
- package/scripts/uniswap/deploy/help.ts +96 -0
- package/scripts/uniswap/deploy/init.ts +70 -0
- package/src/auction/chainXAuction.ts +209 -0
- package/src/auction/chainYVault.ts +153 -0
- package/src/auction/event.ts +19 -0
- package/src/auction/serialize.ts +162 -0
- package/src/auction/type.ts +71 -0
- package/src/lib/signer.ts +20 -0
- package/src/lib/unlock.ts +14 -0
- package/src/uniswap/1-marketprice/addLiquidity.ts +80 -0
- package/src/uniswap/1-marketprice/removeLiquidity.ts +63 -0
- package/src/uniswap/1-marketprice/swap.ts +100 -0
- package/src/uniswap/2-limitorder/kill.ts +70 -0
- package/src/uniswap/2-limitorder/place.ts +93 -0
- package/src/uniswap/2-limitorder/withdraw.ts +78 -0
- package/src/uniswap/3-dynamicfee/dynamicfee.ts +321 -0
- package/src/uniswap/lib/ERC20.ts +49 -0
- package/src/uniswap/lib/contract.ts +18 -0
- package/src/uniswap/lib/limitOrder.ts +40 -0
- package/src/uniswap/lib/liqCalculation.ts +152 -0
- package/src/uniswap/lib/listen.ts +57 -0
- package/src/uniswap/lib/pool.ts +62 -0
- package/src/uniswap/lib/swap.ts +8 -0
- package/src/uniswap/lib/types.ts +21 -0
- package/src/uniswap/lib/utils.ts +26 -0
- package/src/uniswap/playgroud/abiencode.ts +21 -0
- package/src/uniswap/playgroud/amount0.ts +47 -0
- package/src/uniswap/playgroud/errordecode.ts +54 -0
- package/src/uniswap/playgroud/errorsigs.ts +86 -0
- package/src/voucher.ts +122 -0
- package/test/auction/ChainXAuctionV2.test.ts +265 -0
- package/test/auction/ChainYVaultV2.test.js +163 -0
- package/test/auction/ChainYVaultV2.test.ts +183 -0
- package/test/auction/auction.test.ts +106 -0
- package/test/connect_punk.test.ts +26 -0
- package/test/create2.test.ts +44 -0
- package/test/normal.ts +43 -0
- package/test/test-config.ts +18 -0
- package/test/uniswap/example.test.ts +62 -0
- package/test/uniswap/limitOrder.test.ts +184 -0
- package/test/uniswap/mockERC20.test.ts +142 -0
- package/test/voucher_hardhat.test.ts +120 -0
- package/test/voucher_punk.test.ts +83 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
5
|
+
|
|
6
|
+
contract ChainXWrappedETH is ReentrancyGuard {
|
|
7
|
+
enum TokenType { TRANSFER, TRADE }
|
|
8
|
+
|
|
9
|
+
// 记录每个地址两种类型的余额
|
|
10
|
+
mapping(address => uint256) public transferBalances;
|
|
11
|
+
mapping(address => uint256) public tradeBalances;
|
|
12
|
+
|
|
13
|
+
// 只允许存入 transfer 类型
|
|
14
|
+
function deposit() external payable nonReentrant {
|
|
15
|
+
require(msg.value > 0, "Must deposit ETH");
|
|
16
|
+
|
|
17
|
+
transferBalances[msg.sender] += msg.value;
|
|
18
|
+
emit Deposited(msg.sender, msg.value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 转账指定类型的代币
|
|
22
|
+
function transfer(
|
|
23
|
+
address to,
|
|
24
|
+
uint256 amount,
|
|
25
|
+
TokenType fromType,
|
|
26
|
+
TokenType toType
|
|
27
|
+
) external nonReentrant returns (bool) {
|
|
28
|
+
if (fromType == TokenType.TRANSFER) {
|
|
29
|
+
require(transferBalances[msg.sender] >= amount, "Insufficient transfer balance");
|
|
30
|
+
transferBalances[msg.sender] -= amount;
|
|
31
|
+
|
|
32
|
+
// 如果目标类型是 TRADE,则转为 trade.ETH
|
|
33
|
+
if (toType == TokenType.TRADE) {
|
|
34
|
+
tradeBalances[to] += amount;
|
|
35
|
+
emit Transfer(msg.sender, to, amount, TokenType.TRANSFER, TokenType.TRADE);
|
|
36
|
+
} else {
|
|
37
|
+
transferBalances[to] += amount;
|
|
38
|
+
emit Transfer(msg.sender, to, amount, TokenType.TRANSFER, TokenType.TRANSFER);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
require(tradeBalances[msg.sender] >= amount, "Insufficient trade balance");
|
|
42
|
+
tradeBalances[msg.sender] -= amount;
|
|
43
|
+
|
|
44
|
+
// trade.ETH 只能转为 trans.ETH
|
|
45
|
+
transferBalances[to] += amount;
|
|
46
|
+
emit Transfer(msg.sender, to, amount, TokenType.TRADE, TokenType.TRANSFER);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 只允许提取 transfer 类型的 ETH
|
|
53
|
+
function withdraw(uint256 amount) external nonReentrant {
|
|
54
|
+
require(transferBalances[msg.sender] >= amount, "Insufficient transfer balance");
|
|
55
|
+
|
|
56
|
+
transferBalances[msg.sender] -= amount;
|
|
57
|
+
|
|
58
|
+
(bool success, ) = msg.sender.call{value: amount}("");
|
|
59
|
+
require(success, "ETH transfer failed");
|
|
60
|
+
|
|
61
|
+
emit Withdrawn(msg.sender, amount);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 查询地址的两种代币余额
|
|
65
|
+
function balanceOf(address account) external view returns (uint256 transferBalance, uint256 tradeBalance) {
|
|
66
|
+
return (transferBalances[account], tradeBalances[account]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
event Deposited(address indexed user, uint256 amount);
|
|
70
|
+
event Transfer(
|
|
71
|
+
address indexed from,
|
|
72
|
+
address indexed to,
|
|
73
|
+
uint256 amount,
|
|
74
|
+
TokenType fromType,
|
|
75
|
+
TokenType toType
|
|
76
|
+
);
|
|
77
|
+
event Withdrawn(address indexed user, uint256 amount);
|
|
78
|
+
|
|
79
|
+
receive() external payable {}
|
|
80
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "./ChainYShadowETH.sol";
|
|
5
|
+
import "./interfaces/IUniswapV2Router02.sol";
|
|
6
|
+
|
|
7
|
+
contract ChainYLiquidityManager {
|
|
8
|
+
ChainYShadowETH public immutable shadowETH;
|
|
9
|
+
IUniswapV2Router02 public immutable router;
|
|
10
|
+
IUniswapV2Pair public immutable pair;
|
|
11
|
+
|
|
12
|
+
constructor(address payable _shadowETH, address _router) {
|
|
13
|
+
shadowETH = ChainYShadowETH(_shadowETH);
|
|
14
|
+
router = IUniswapV2Router02(_router);
|
|
15
|
+
|
|
16
|
+
// 初始化交易对
|
|
17
|
+
pair = IUniswapV2Pair(
|
|
18
|
+
IUniswapV2Factory(router.factory()).getPair(_shadowETH, router.WETH())
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function addLiquidity(
|
|
23
|
+
uint256 amount,
|
|
24
|
+
address to
|
|
25
|
+
) external payable {
|
|
26
|
+
shadowETH.transferFrom(msg.sender, address(this), amount);
|
|
27
|
+
shadowETH.approve(address(router), amount);
|
|
28
|
+
|
|
29
|
+
router.addLiquidityETH{value: msg.value}(
|
|
30
|
+
address(shadowETH),
|
|
31
|
+
amount,
|
|
32
|
+
amount * 99 / 100,
|
|
33
|
+
msg.value * 99 / 100,
|
|
34
|
+
to,
|
|
35
|
+
block.timestamp
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function removeLiquidity(
|
|
40
|
+
uint256 liquidity,
|
|
41
|
+
address to
|
|
42
|
+
) external {
|
|
43
|
+
require(pair.transferFrom(msg.sender, address(this), liquidity), "LP transfer failed");
|
|
44
|
+
require(pair.approve(address(router), liquidity), "LP approve failed");
|
|
45
|
+
|
|
46
|
+
router.removeLiquidityETH(
|
|
47
|
+
address(shadowETH),
|
|
48
|
+
liquidity,
|
|
49
|
+
0,
|
|
50
|
+
0,
|
|
51
|
+
to,
|
|
52
|
+
block.timestamp
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
receive() external payable {}
|
|
57
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
6
|
+
import "./interfaces/IUniswapV2Router02.sol";
|
|
7
|
+
import "./interfaces/IUniswapV2Pair.sol";
|
|
8
|
+
import "./interfaces/IUniswapV2Factory.sol";
|
|
9
|
+
import "./utils/RLPReader.sol";
|
|
10
|
+
import "./ChainYLiquidityManager.sol";
|
|
11
|
+
|
|
12
|
+
contract ChainYShadowETH is ERC20, ReentrancyGuard {
|
|
13
|
+
using RLPReader for bytes;
|
|
14
|
+
using RLPReader for RLPReader.RLPItem;
|
|
15
|
+
|
|
16
|
+
enum TokenType { TRANSFER, TRADE }
|
|
17
|
+
|
|
18
|
+
// 状态变量
|
|
19
|
+
mapping(bytes32 => bool) public processedReceipts;
|
|
20
|
+
|
|
21
|
+
// Uniswap相关
|
|
22
|
+
IUniswapV2Router02 public immutable router;
|
|
23
|
+
IUniswapV2Pair public immutable pair;
|
|
24
|
+
ChainYLiquidityManager public immutable liquidityManager;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
address _router,
|
|
28
|
+
address payable _liquidityManager
|
|
29
|
+
) ERC20("Shadow ETH", "sETH") {
|
|
30
|
+
router = IUniswapV2Router02(_router);
|
|
31
|
+
liquidityManager = ChainYLiquidityManager(_liquidityManager);
|
|
32
|
+
|
|
33
|
+
pair = IUniswapV2Pair(
|
|
34
|
+
IUniswapV2Factory(router.factory()).getPair(address(this), router.WETH())
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 主要的收据处理函数
|
|
39
|
+
function handleReceipt(bytes memory receipt) external nonReentrant {
|
|
40
|
+
require(verifyAndMarkReceipt(receipt), "Invalid proof");
|
|
41
|
+
|
|
42
|
+
// 解析以太坊收据
|
|
43
|
+
RLPReader.RLPItem[] memory items = receipt.toRlpItem().toList();
|
|
44
|
+
require(items.length >= 4, "Invalid receipt format");
|
|
45
|
+
|
|
46
|
+
// 解析日志数组
|
|
47
|
+
bytes memory logs = items[3].toBytes();
|
|
48
|
+
RLPReader.RLPItem[] memory logItems = logs.toRlpItem().toList();
|
|
49
|
+
require(logItems.length > 0, "No logs found");
|
|
50
|
+
|
|
51
|
+
// 遍历所有日志
|
|
52
|
+
for(uint i = 0; i < logItems.length; i++) {
|
|
53
|
+
RLPReader.RLPItem[] memory logParts = logItems[i].toList();
|
|
54
|
+
require(logParts.length >= 3, "Invalid log format");
|
|
55
|
+
|
|
56
|
+
// 获取事件主题和数据
|
|
57
|
+
bytes32 eventTopic = bytes32(logParts[1].toUint());
|
|
58
|
+
bytes memory eventData = logParts[2].toBytes();
|
|
59
|
+
|
|
60
|
+
if (eventTopic == keccak256("Deposited(address,uint256)")) {
|
|
61
|
+
(address user, uint256 amount) = abi.decode(
|
|
62
|
+
eventData,
|
|
63
|
+
(address, uint256)
|
|
64
|
+
);
|
|
65
|
+
handleDeposit(user, amount);
|
|
66
|
+
|
|
67
|
+
} else if (eventTopic == keccak256("Transfer(address,address,uint256,uint8,uint8)")) {
|
|
68
|
+
(
|
|
69
|
+
address from,
|
|
70
|
+
address to,
|
|
71
|
+
uint256 amount,
|
|
72
|
+
TokenType fromType,
|
|
73
|
+
TokenType toType
|
|
74
|
+
) = abi.decode(
|
|
75
|
+
eventData,
|
|
76
|
+
(address, address, uint256, TokenType, TokenType)
|
|
77
|
+
);
|
|
78
|
+
handleTransfer(from, to, amount, fromType, toType);
|
|
79
|
+
|
|
80
|
+
} else if (eventTopic == keccak256("Withdrawn(address,uint256)")) {
|
|
81
|
+
(address user, uint256 amount) = abi.decode(
|
|
82
|
+
eventData,
|
|
83
|
+
(address, uint256)
|
|
84
|
+
);
|
|
85
|
+
handleWithdraw(user, amount);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 处理存款事件
|
|
91
|
+
function handleDeposit(
|
|
92
|
+
address user,
|
|
93
|
+
uint256 amount
|
|
94
|
+
) internal {
|
|
95
|
+
_mint(user, amount);
|
|
96
|
+
emit ShadowMinted(user, amount);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 处理转账事件
|
|
100
|
+
function handleTransfer(
|
|
101
|
+
address from,
|
|
102
|
+
address to,
|
|
103
|
+
uint256 amount,
|
|
104
|
+
TokenType fromType,
|
|
105
|
+
TokenType toType
|
|
106
|
+
) internal {
|
|
107
|
+
if (fromType == TokenType.TRANSFER && toType == TokenType.TRANSFER) {
|
|
108
|
+
_transfer(from, to, amount);
|
|
109
|
+
}
|
|
110
|
+
else if (fromType == TokenType.TRANSFER && toType == TokenType.TRADE) {
|
|
111
|
+
_approve(from, address(liquidityManager), amount);
|
|
112
|
+
liquidityManager.addLiquidity{value: amount}(amount, to);
|
|
113
|
+
}
|
|
114
|
+
else if (fromType == TokenType.TRADE && toType == TokenType.TRANSFER) {
|
|
115
|
+
uint256 liquidity = pair.balanceOf(from);
|
|
116
|
+
require(liquidity >= amount, "Insufficient LP tokens");
|
|
117
|
+
liquidityManager.removeLiquidity(liquidity, to);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 处理提现事件
|
|
122
|
+
function handleWithdraw(
|
|
123
|
+
address user,
|
|
124
|
+
uint256 amount
|
|
125
|
+
) internal {
|
|
126
|
+
_burn(user, amount);
|
|
127
|
+
emit ShadowBurned(user, amount);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 验证并标记收据
|
|
131
|
+
function verifyAndMarkReceipt(bytes memory receipt) internal returns (bool) {
|
|
132
|
+
bytes32 receiptHash = keccak256(receipt);
|
|
133
|
+
|
|
134
|
+
require(!processedReceipts[receiptHash], "Receipt already processed");
|
|
135
|
+
|
|
136
|
+
// todo
|
|
137
|
+
|
|
138
|
+
processedReceipts[receiptHash] = true;
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 接收ETH
|
|
143
|
+
receive() external payable {}
|
|
144
|
+
|
|
145
|
+
// 事件
|
|
146
|
+
event ShadowMinted(address indexed user, uint256 amount);
|
|
147
|
+
event ShadowBurned(address indexed user, uint256 amount);
|
|
148
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
5
|
+
import "./utils/RLPReader.sol";
|
|
6
|
+
|
|
7
|
+
contract ChainYVault is ReentrancyGuard {
|
|
8
|
+
using RLPReader for bytes;
|
|
9
|
+
using RLPReader for RLPReader.RLPItem;
|
|
10
|
+
|
|
11
|
+
struct AuctionLock {
|
|
12
|
+
address owner;
|
|
13
|
+
bytes32 hashSecret;
|
|
14
|
+
uint256 unlockTime;
|
|
15
|
+
bool isLocked;
|
|
16
|
+
uint256 amount;
|
|
17
|
+
bool secretRevealed;
|
|
18
|
+
uint256 auctionEndTime;
|
|
19
|
+
address highestBidder;
|
|
20
|
+
uint256 highestBid;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
uint256 public baseAmount;
|
|
24
|
+
uint256 public auctionDuration;
|
|
25
|
+
mapping(bytes32 => AuctionLock) public lockedTokens;
|
|
26
|
+
address public admin;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
admin = msg.sender;
|
|
30
|
+
baseAmount = 100 * (10 ** 18); // 初始值 100 PUNK
|
|
31
|
+
auctionDuration = 1 hours;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function setBaseAmount(uint256 newAmount) external {
|
|
35
|
+
require(msg.sender == admin, "Only admin can set base amount");
|
|
36
|
+
require(newAmount > 0, "Base amount must be greater than 0");
|
|
37
|
+
baseAmount = newAmount;
|
|
38
|
+
emit BaseAmountUpdated(newAmount);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function setAuctionDuration(uint256 newDuration) external {
|
|
42
|
+
require(msg.sender == admin, "Only admin can set auction duration");
|
|
43
|
+
require(newDuration > 0, "Auction duration must be greater than 0");
|
|
44
|
+
auctionDuration = newDuration;
|
|
45
|
+
emit AuctionDurationUpdated(newDuration);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function lockTokens(
|
|
49
|
+
bytes32 hashSecret,
|
|
50
|
+
uint256 revealTimestamp
|
|
51
|
+
) external payable nonReentrant returns (bytes32 lockId) {
|
|
52
|
+
require(msg.value > 0, "Amount must be greater than 0");
|
|
53
|
+
require(msg.value % baseAmount == 0, "Amount must be multiple of baseAmount");
|
|
54
|
+
|
|
55
|
+
// 生成锁定ID
|
|
56
|
+
lockId = keccak256(abi.encodePacked(
|
|
57
|
+
msg.sender,
|
|
58
|
+
hashSecret,
|
|
59
|
+
revealTimestamp
|
|
60
|
+
));
|
|
61
|
+
|
|
62
|
+
require(!lockedTokens[lockId].isLocked, "Lock ID already exists");
|
|
63
|
+
|
|
64
|
+
// 记录锁定信息
|
|
65
|
+
lockedTokens[lockId] = AuctionLock({
|
|
66
|
+
owner: msg.sender,
|
|
67
|
+
hashSecret: hashSecret,
|
|
68
|
+
unlockTime: revealTimestamp,
|
|
69
|
+
auctionEndTime: revealTimestamp + auctionDuration,
|
|
70
|
+
isLocked: true,
|
|
71
|
+
amount: msg.value,
|
|
72
|
+
secretRevealed: false,
|
|
73
|
+
highestBidder: address(0),
|
|
74
|
+
highestBid: 0
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
emit TokensLocked(lockId, msg.sender, msg.value, hashSecret);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function updateHighestBid(bytes32 lockId, uint256 newBid) external {
|
|
81
|
+
AuctionLock storage lock = lockedTokens[lockId];
|
|
82
|
+
require(lock.isLocked, "Tokens not locked");
|
|
83
|
+
require(newBid > lock.highestBid, "New bid must be higher than current highest bid");
|
|
84
|
+
lock.highestBid = newBid;
|
|
85
|
+
lock.highestBidder = msg.sender;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function unlockTokens(bytes32 lockId, bytes memory receipt) external nonReentrant {
|
|
89
|
+
AuctionLock storage lock = lockedTokens[lockId];
|
|
90
|
+
require(lock.isLocked, "Tokens not locked");
|
|
91
|
+
|
|
92
|
+
// 解析以太坊收据
|
|
93
|
+
RLPReader.RLPItem[] memory items = receipt.toRlpItem().toList();
|
|
94
|
+
require(items.length >= 4, "Invalid receipt format");
|
|
95
|
+
|
|
96
|
+
// 收据结构: [status, cumulativeGasUsed, logsBloom, logs[]]
|
|
97
|
+
bytes memory logs = items[3].toBytes();
|
|
98
|
+
RLPReader.RLPItem[] memory logItems = logs.toRlpItem().toList();
|
|
99
|
+
require(logItems.length > 0, "No logs found");
|
|
100
|
+
|
|
101
|
+
// 解析日志
|
|
102
|
+
RLPReader.RLPItem[] memory logParts = logItems[0].toList();
|
|
103
|
+
require(logParts.length >= 3, "Invalid log format");
|
|
104
|
+
|
|
105
|
+
// 获取事件主题和数据
|
|
106
|
+
bytes32 eventTopic = bytes32(logParts[1].toUint());
|
|
107
|
+
bytes memory eventData = logParts[2].toBytes();
|
|
108
|
+
if (eventTopic == keccak256("AuctionFinalized(bytes32,address,uint256)")) {
|
|
109
|
+
(bytes32 auctionId, address winner,) = abi.decode(
|
|
110
|
+
eventData,
|
|
111
|
+
(bytes32, address, uint256)
|
|
112
|
+
);
|
|
113
|
+
require(auctionId == lockId, "Invalid auction ID");
|
|
114
|
+
|
|
115
|
+
lock.isLocked = false;
|
|
116
|
+
|
|
117
|
+
if (winner != address(0)) {
|
|
118
|
+
// 有效出价,转给竞拍者
|
|
119
|
+
(bool success, ) = winner.call{value: lock.amount}("");
|
|
120
|
+
require(success, "Transfer failed");
|
|
121
|
+
emit TokensUnlocked(lockId, winner, lock.amount);
|
|
122
|
+
} else {
|
|
123
|
+
// 竞拍者地址为0,退回给原主人
|
|
124
|
+
(bool success, ) = lock.owner.call{value: lock.amount}("");
|
|
125
|
+
require(success, "Transfer failed");
|
|
126
|
+
emit TokensUnlocked(lockId, lock.owner, lock.amount);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
} else if (eventTopic == keccak256("AuctionCancelled(bytes32)")) {
|
|
130
|
+
(bytes32 auctionId) = abi.decode(eventData, (bytes32));
|
|
131
|
+
require(auctionId == lockId, "Invalid auction ID");
|
|
132
|
+
|
|
133
|
+
// 永久锁定
|
|
134
|
+
emit TokensPermanentlyLocked(lockId, lock.amount);
|
|
135
|
+
} else {
|
|
136
|
+
revert("Invalid event");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/*
|
|
141
|
+
function unlockTokens(bytes32 lockId, bytes32 secret) external nonReentrant {
|
|
142
|
+
AuctionLock storage lock = lockedTokens[lockId];
|
|
143
|
+
require(lock.isLocked, "Tokens not locked");
|
|
144
|
+
|
|
145
|
+
if (!lock.secretRevealed) {
|
|
146
|
+
// 第一阶段:必须在 T_1 前公开 secret
|
|
147
|
+
require(block.timestamp <= lock.unlockTime, "Secret not revealed before T_1, tokens locked forever");
|
|
148
|
+
require(lock.owner == msg.sender, "Not the owner");
|
|
149
|
+
require(keccak256(abi.encodePacked(secret)) == lock.hashSecret, "Invalid secret");
|
|
150
|
+
|
|
151
|
+
lock.secretRevealed = true;
|
|
152
|
+
lock.auctionEndTime = block.timestamp + auctionDuration;
|
|
153
|
+
emit SecretRevealed(lockId, uint256(secret));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 第二阶段:等待链 X 同步最高出价或超时解锁
|
|
158
|
+
require(block.timestamp > lock.auctionEndTime, "Auction period not ended");
|
|
159
|
+
|
|
160
|
+
if (lock.highestBid > uint256(secret) && lock.highestBidder != address(0)) {
|
|
161
|
+
// 有效出价,转给竞拍者
|
|
162
|
+
lock.isLocked = false;
|
|
163
|
+
(bool success, ) = lock.highestBidder.call{value: lock.amount}("");
|
|
164
|
+
require(success, "Transfer failed");
|
|
165
|
+
emit TokensUnlocked(lockId, lock.highestBidder, lock.amount);
|
|
166
|
+
} else {
|
|
167
|
+
// 无有效出价,退还给原主人
|
|
168
|
+
lock.isLocked = false;
|
|
169
|
+
(bool success, ) = lock.owner.call{value: lock.amount}("");
|
|
170
|
+
require(success, "Transfer failed");
|
|
171
|
+
emit TokensUnlocked(lockId, lock.owner, lock.amount);
|
|
172
|
+
}
|
|
173
|
+
}*/
|
|
174
|
+
|
|
175
|
+
// 接收原生代币的回退函数
|
|
176
|
+
receive() external payable {}
|
|
177
|
+
|
|
178
|
+
event TokensLocked(
|
|
179
|
+
bytes32 indexed lockId,
|
|
180
|
+
address indexed owner,
|
|
181
|
+
uint256 amount,
|
|
182
|
+
bytes32 hashSecret
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
event TokensUnlocked(
|
|
186
|
+
bytes32 indexed lockId,
|
|
187
|
+
address indexed owner,
|
|
188
|
+
uint256 amount
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
event BaseAmountUpdated(uint256 newAmount);
|
|
192
|
+
event SecretRevealed(bytes32 indexed lockId, uint256 secret);
|
|
193
|
+
event AuctionDurationUpdated(uint256 newDuration);
|
|
194
|
+
event TokensPermanentlyLocked(bytes32 indexed lockId, uint256 amount);
|
|
195
|
+
}
|