1+ //SPDX-License-Identifier: MIT
2+ pragma solidity ^ 0.8.0 ;
3+
4+ import "@openzeppelin/contracts/access/Ownable.sol " ;
5+
6+ contract YakuSwap is Ownable {
7+
8+ // Uninitialized - Default status (if swaps[index] doesn't exist, status will get this value)
9+ // Created - the swap was created, but the mone is still in the contract
10+ // Completed - the money has been sent to 'toAddress' (swap successful)
11+ // Cancelled - the money has been sent to 'fromAddress' (maxBlockHeight was reached)
12+ enum SwapStatus {Uninitialized, Created, Completed, Cancelled}
13+
14+ mapping (bytes32 => SwapStatus) public swaps;
15+
16+ uint public totalFees = 0 ;
17+
18+ uint constant public MAX_BLOCK_HEIGHT = 256 ;
19+
20+ event SwapCreated (
21+ bytes32 indexed swapHash ,
22+ address indexed fromAddress ,
23+ address indexed toAddress ,
24+ uint value ,
25+ bytes32 secretHash ,
26+ uint blockNumber
27+ );
28+
29+ function _getSwapHash (
30+ address fromAddress ,
31+ address toAddress ,
32+ uint value ,
33+ bytes32 secretHash ,
34+ uint blockNumber
35+ ) internal view returns (bytes32 ) {
36+ return keccak256 (
37+ abi.encode (
38+ fromAddress,
39+ toAddress,
40+ value,
41+ secretHash,
42+ blockNumber,
43+ block .chainid
44+ )
45+ );
46+ }
47+
48+ function getSwapHash (
49+ address fromAddress ,
50+ address toAddress ,
51+ uint value ,
52+ bytes32 secretHash ,
53+ uint blockNumber
54+ ) external view returns (bytes32 ) {
55+ return _getSwapHash (
56+ fromAddress, toAddress, value, secretHash, blockNumber
57+ );
58+ }
59+
60+ function createSwap (address toAddress , bytes32 secretHash ) payable external {
61+ require (toAddress != address (0 ), "Destination address cannot be zero " );
62+
63+ bytes32 swapHash = _getSwapHash (
64+ msg .sender ,
65+ toAddress,
66+ msg .value ,
67+ secretHash,
68+ block .number
69+ );
70+
71+ require (swaps[swapHash] == SwapStatus.Uninitialized);
72+ swaps[swapHash] = SwapStatus.Created;
73+
74+ emit SwapCreated (
75+ swapHash,
76+ msg .sender ,
77+ toAddress,
78+ msg .value ,
79+ secretHash,
80+ block .number
81+ );
82+ }
83+
84+ function completeSwap (
85+ address fromAddress ,
86+ address toAddress ,
87+ uint value ,
88+ uint blockNumber ,
89+ string memory secret
90+ ) external {
91+ bytes32 secretHash = sha256 (abi.encodePacked (secret));
92+ bytes32 swapHash = _getSwapHash (
93+ fromAddress,
94+ toAddress,
95+ value,
96+ secretHash,
97+ blockNumber
98+ );
99+
100+ require (swaps[swapHash] == SwapStatus.Created, "Invalid swap data or swap already completed " );
101+ require (block .number < blockNumber + MAX_BLOCK_HEIGHT, "Deadline exceeded " );
102+ swaps[swapHash] = SwapStatus.Completed;
103+
104+ uint swapAmount = value * 993 / 1000 ;
105+ totalFees += value - swapAmount;
106+
107+ (bool success ,) = toAddress.call {value : swapAmount}("" );
108+ require (success, "Transfer failed " );
109+ }
110+
111+ function cancelSwap (
112+ address toAddress ,
113+ uint value ,
114+ bytes32 secretHash ,
115+ uint blockNumber
116+ ) public {
117+ bytes32 swapHash = _getSwapHash (
118+ msg .sender ,
119+ toAddress,
120+ value,
121+ secretHash,
122+ blockNumber
123+ );
124+
125+ require (swaps[swapHash] == SwapStatus.Created, "Invalid swap status " );
126+ require (block .number >= blockNumber + MAX_BLOCK_HEIGHT, "MAX_BLOCK_HEIGHT not exceeded " );
127+
128+ swaps[swapHash] = SwapStatus.Cancelled;
129+ (bool success ,) = msg .sender .call {value : value}("" );
130+ require (success, "Transfer failed " );
131+ }
132+
133+ function withdrawFees () external onlyOwner {
134+ uint feesToWithdraw = totalFees;
135+ totalFees = 0 ;
136+
137+ (bool success ,) = owner ().call {value : feesToWithdraw}("" );
138+ require (success, "Transfer failed " );
139+ }
140+ }
0 commit comments