-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathCBridge.sol
170 lines (150 loc) · 4.85 KB
/
CBridge.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract CBridge {
using SafeERC20 for IERC20;
enum TransferStatus {
Null,
Pending,
Confirmed,
Refunded
}
struct Transfer {
address sender;
address receiver;
address token;
uint256 amount;
bytes32 hashlock; // hash of the preimage
uint64 timelock; // UNIX timestamp seconds - locked UNTIL this time
TransferStatus status;
}
mapping(bytes32 => Transfer) public transfers;
event LogNewTransferOut(
bytes32 transferId,
address sender,
address receiver,
address token,
uint256 amount,
bytes32 hashlock, // hash of the preimage
uint64 timelock, // UNIX timestamp seconds - locked UNTIL this time
uint64 dstChainId,
address dstAddress
);
event LogNewTransferIn(
bytes32 transferId,
address sender,
address receiver,
address token,
uint256 amount,
bytes32 hashlock, // hash of the preimage
uint64 timelock, // UNIX timestamp seconds - locked UNTIL this time
uint64 srcChainId,
bytes32 srcTransferId // outbound transferId at src chain
);
event LogTransferConfirmed(bytes32 transferId, bytes32 preimage);
event LogTransferRefunded(bytes32 transferId);
/**
* @dev transfer sets up a new outbound transfer with hash time lock.
*/
function transferOut(
address _bridge,
address _token,
uint256 _amount,
bytes32 _hashlock,
uint64 _timelock,
uint64 _dstChainId,
address _dstAddress
) external {
bytes32 transferId = _transfer(_bridge, _token, _amount, _hashlock, _timelock);
emit LogNewTransferOut(
transferId,
msg.sender,
_bridge,
_token,
_amount,
_hashlock,
_timelock,
_dstChainId,
_dstAddress
);
}
/**
* @dev transfer sets up a new inbound transfer with hash time lock.
*/
function transferIn(
address _dstAddress,
address _token,
uint256 _amount,
bytes32 _hashlock,
uint64 _timelock,
uint64 _srcChainId,
bytes32 _srcTransferId
) external {
bytes32 transferId = _transfer(_dstAddress, _token, _amount, _hashlock, _timelock);
emit LogNewTransferIn(
transferId,
msg.sender,
_dstAddress,
_token,
_amount,
_hashlock,
_timelock,
_srcChainId,
_srcTransferId
);
}
/**
* @dev confirm a transfer.
*
* @param _transferId Id of pending transfer.
* @param _preimage key for the hashlock
*/
function confirm(bytes32 _transferId, bytes32 _preimage) external {
Transfer memory t = transfers[_transferId];
require(t.status == TransferStatus.Pending, "not pending transfer");
require(t.hashlock == keccak256(abi.encodePacked(_preimage)), "incorrect preimage");
transfers[_transferId].status = TransferStatus.Confirmed;
IERC20(t.token).safeTransfer(t.receiver, t.amount);
emit LogTransferConfirmed(_transferId, _preimage);
}
/**
* @dev refund a transfer after timeout.
*
* @param _transferId Id of pending transfer.
*/
function refund(bytes32 _transferId) external {
Transfer memory t = transfers[_transferId];
require(t.status == TransferStatus.Pending, "not pending transfer");
require(t.timelock <= block.timestamp, "timelock not yet passed");
transfers[_transferId].status = TransferStatus.Refunded;
IERC20(t.token).safeTransfer(t.sender, t.amount);
emit LogTransferRefunded(_transferId);
}
/**
* @dev transfer sets up a new transfer with hash time lock.
*/
function _transfer(
address _receiver,
address _token,
uint256 _amount,
bytes32 _hashlock,
uint64 _timelock
) private returns (bytes32 transferId) {
require(_amount > 0, "invalid amount");
require(_timelock > block.timestamp, "invalid timelock");
transferId = keccak256(abi.encodePacked(msg.sender, _receiver, _hashlock, block.chainid));
require(transfers[transferId].status == TransferStatus.Null, "transfer exists");
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
transfers[transferId] = Transfer(
msg.sender,
_receiver,
_token,
_amount,
_hashlock,
_timelock,
TransferStatus.Pending
);
return transferId;
}
}