-
Notifications
You must be signed in to change notification settings - Fork 231
/
Copy pathAtomicSwap.sol
187 lines (163 loc) · 5.15 KB
/
AtomicSwap.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright (c) 2017 Altcoin Exchange, Inc
// Copyright (c) 2018 BetterToken BVBA
// Use of this source code is governed by an MIT
// license that can be found at https://github.com/rivine/rivine/blob/master/LICENSE.
pragma solidity ^0.4.23;
// Notes on security warnings:
// + block.timestamp is safe to use,
// given that our timestamp can tolerate a 30-second drift in time;
contract AtomicSwap {
enum Kind { Initiator, Participant }
enum State { Empty, Filled, Redeemed, Refunded }
struct Swap {
uint initTimestamp;
uint refundTime;
bytes32 secretHash;
bytes32 secret;
address initiator;
address participant;
uint256 value;
Kind kind;
State state;
}
mapping(bytes32 => Swap) public swaps;
event Refunded(
uint refundTime,
bytes32 secretHash,
address refunder,
uint256 value
);
event Redeemed(
uint redeemTime,
bytes32 secretHash,
bytes32 secret,
address redeemer,
uint256 value
);
event Participated(
uint initTimestamp,
uint refundTime,
bytes32 secretHash,
address initiator,
address participant,
uint256 value
);
event Initiated(
uint initTimestamp,
uint refundTime,
bytes32 secretHash,
address initiator,
address participant,
uint256 value
);
constructor() public {}
modifier isRefundable(bytes32 secretHash, address refunder) {
require(swaps[secretHash].state == State.Filled);
if (swaps[secretHash].kind == Kind.Participant) {
require(swaps[secretHash].participant == refunder);
} else {
require(swaps[secretHash].initiator == refunder);
}
uint preRefundTimestamp = swaps[secretHash].initTimestamp;
preRefundTimestamp += swaps[secretHash].refundTime;
require(block.timestamp > preRefundTimestamp);
_;
}
modifier isRedeemable(bytes32 secretHash, bytes32 secret, address redeemer) {
require(swaps[secretHash].state == State.Filled);
if (swaps[secretHash].kind == Kind.Participant) {
require(swaps[secretHash].initiator == redeemer);
} else {
require(swaps[secretHash].participant == redeemer);
}
require(sha256(abi.encodePacked(secret)) == secretHash);
_;
}
modifier isInitiator(bytes32 secretHash) {
require(msg.sender == swaps[secretHash].initiator);
_;
}
modifier isNotInitiated(bytes32 secretHash) {
require(swaps[secretHash].state == State.Empty);
_;
}
modifier hasNoNilValues(uint refundTime) {
require(msg.value > 0);
require(refundTime > 0);
_;
}
function initiate(uint refundTime, bytes32 secretHash, address participant)
public
payable
hasNoNilValues(refundTime)
isNotInitiated(secretHash)
{
swaps[secretHash].initTimestamp = block.timestamp;
swaps[secretHash].refundTime = refundTime;
swaps[secretHash].secretHash = secretHash;
swaps[secretHash].initiator = msg.sender;
swaps[secretHash].participant = participant;
swaps[secretHash].value = msg.value;
swaps[secretHash].kind = Kind.Initiator;
swaps[secretHash].state = State.Filled;
emit Initiated(
block.timestamp,
refundTime,
secretHash,
msg.sender,
participant,
msg.value
);
}
function participate(uint refundTime, bytes32 secretHash, address initiator)
public
payable
hasNoNilValues(refundTime)
isNotInitiated(secretHash)
{
swaps[secretHash].initTimestamp = block.timestamp;
swaps[secretHash].refundTime = refundTime;
swaps[secretHash].secretHash = secretHash;
swaps[secretHash].initiator = initiator;
swaps[secretHash].participant = msg.sender;
swaps[secretHash].value = msg.value;
swaps[secretHash].kind = Kind.Participant;
swaps[secretHash].state = State.Filled;
emit Participated(
block.timestamp,
refundTime,
secretHash,
initiator,
msg.sender,
msg.value
);
}
function redeem(bytes32 secret, bytes32 secretHash)
public
isRedeemable(secretHash, secret, msg.sender)
{
msg.sender.transfer(swaps[secretHash].value);
swaps[secretHash].state = State.Redeemed;
swaps[secretHash].secret = secret;
emit Redeemed(
block.timestamp,
swaps[secretHash].secretHash,
swaps[secretHash].secret,
msg.sender,
swaps[secretHash].value
);
}
function refund(bytes32 secretHash)
public
isRefundable(secretHash, msg.sender)
{
msg.sender.transfer(swaps[secretHash].value);
swaps[secretHash].state = State.Refunded;
emit Refunded(
block.timestamp,
swaps[secretHash].secretHash,
msg.sender,
swaps[secretHash].value
);
}
}