-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathescrow.cpp
More file actions
238 lines (211 loc) · 8.93 KB
/
escrow.cpp
File metadata and controls
238 lines (211 loc) · 8.93 KB
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/time.hpp>
using namespace eosio;
using namespace std;
time_point current_time_point();
class [[eosio::contract("dep")]] dep : public contract {
public:
using contract::contract;
static constexpr uint32_t refund_delay_sec = 30;
const asset zero = asset(0, symbol("SYS", 4));
// listens for transfer to smartcontract's EOS account
// and populates deposits with sent money;
// memo should be seller account name.
[[eosio::action]]
void transfer(name from, name to, asset quantity, string memo);
// opens deposit to hold tokens from buyer to seller;
// balance is set to zero; right know deposit is identified by
// (buyer, seller) pair which means only one deal between the two
// can be in progress. We should add deposit names in the future.
[[eosio::action]]
void opendeposit(name buyer, name seller);
// buyer can request his money back (in case the seller failed
// to fulfill contract); withdraw money are not transfered immediately
// (buyer should call "refund" action)
[[eosio::action]]
void withdraw(name buyer, name seller);
// seller can claim money hold in escrow; money are not transfered
// immediately to seller account (he should use "refund" method
// to initiate transfer)
[[eosio::action]]
void claim(name buyer, name seller);
// once money are claimed (or withdrawn) seller (or buyer) can
// request refund and initiate transfer to his account;
// transfer can not be initiated before "refund_delay_sec" second after
// clime (withdraw)
[[eosio::action]]
void refund(name buyer, name seller);
// seller or buyer can hold transfers from escrow;
[[eosio::action]]
void hold(name buyer, name seller);
// hold transfers are conflicts that should be resolved by
// arbitrator with "resolve" action; user is the account name
// to which money should be transfered
[[eosio::action]]
void resolve(name buyer, name seller, name user);
private:
struct [[eosio::table]] dep_rec {
asset amount;
name seller;
uint64_t primary_key() const { return seller.value; }
};
struct [[eosio::table]] claim_rec {
name buyer;
name seller;
asset amount;
time_point_sec request_time;
bool is_withdrawal;
bool on_hold;
uint64_t primary_key() const {return seller.value;}
};
typedef eosio::multi_index< "claims"_n, claim_rec > claims;
typedef eosio::multi_index< "deposits"_n, dep_rec > deposits;
void sub_balance(name buyer, name seller, asset value);
void add_balance(name buyer, name seller, asset value);
void create_claim(name buyer, name seller, bool is_withdrawal);
};
void dep::transfer(name from, name to, asset quantity, string memo) {
if (from == _self || to != _self) {
return;
}
eosio_assert(quantity.symbol == symbol("SYS", 4), "I think you're looking for another contract");
eosio_assert(quantity.is_valid(), "Are you trying to corrupt me?");
eosio_assert(quantity.amount > 0, "When pigs fly");
deposits db(_self, from.value);
auto to_acnt = db.find(name(memo).value);
eosio_assert(to_acnt != db.end(), "Don't send us your money before opening account" );
add_balance(from, name(memo), quantity);
}
void dep::opendeposit(name buyer, name seller) {
require_auth(buyer);
deposits db(_self, buyer.value );
auto it = db.find(seller.value);
eosio_assert(it == db.end(), "Depoist already exists");
add_balance(buyer, seller, zero);
}
void dep::withdraw(name buyer, name seller) {
require_auth(buyer);
create_claim(buyer, seller, true);
}
void dep::claim(name buyer, name seller) {
require_auth(seller);
create_claim(buyer, seller, false);
}
void dep::create_claim(name buyer, name seller, bool is_withdrawal) {
deposits dep_db(_self, buyer.value);
auto deposit = dep_db.find(seller.value);
eosio_assert(deposit != dep_db.end(), "Deposit not found");
eosio_assert(seller == deposit->seller, "seller missmatch");
asset amount = deposit->amount;
dep_db.erase(deposit);
claims claims_db(_self, buyer.value);
auto request = claims_db.find(seller.value);
if (request == claims_db.end()) {
claims_db.emplace(is_withdrawal?buyer:seller, [&](auto &claim) {
claim.buyer = buyer;
claim.seller = seller;
claim.request_time = current_time_point();
claim.amount = amount;
claim.is_withdrawal = is_withdrawal;
claim.on_hold = false;
});
} else {
claims_db.modify(request, same_payer, [&](auto &req) {
req.amount += amount;
req.request_time = current_time_point();
});
}
}
void dep::hold(name buyer, name seller) {
claims db(_self, buyer.value);
auto request = db.find(seller.value);
eosio_assert(request != db.end(), "No such claim");
eosio_assert(buyer == request->buyer && seller == request->seller, "seller/buyer missmatch");
name user = request->is_withdrawal?request->seller:request->buyer;
require_auth(user);
if (request->on_hold) return;
db.modify(request, user, [&](auto &req) {
req.on_hold = true;
});
}
void dep::refund(name buyer, name seller) {
claims db(_self, buyer.value);
auto request = db.find(seller.value);
eosio_assert(request != db.end(), "No claim request found");
name account = request->is_withdrawal?buyer:seller;
require_auth(account);
eosio_assert(buyer == request->buyer && seller == request->seller, "seller/buyer mismatch");
eosio_assert(!request->on_hold, "Request is hold");
eosio_assert(request->request_time + seconds(refund_delay_sec) <= current_time_point(),
"Refund is not available yet" );
action transfer = action(
permission_level{get_self() ,"active"_n},
"eosio.token"_n,
"transfer"_n,
std::make_tuple(get_self(), account, request->amount, std::string("Here are your tokens"))
);
transfer.send();
db.erase(request);
}
void dep::resolve(name buyer, name seller, name user) {
require_auth(_self);
claims db(_self, buyer.value);
auto request = db.find(seller.value);
eosio_assert(request != db.end(), "No claim request found");
eosio_assert(buyer == request->buyer && seller == request->seller, "seller/buyer missmatch");
eosio_assert(request->request_time + seconds(refund_delay_sec) <= current_time_point(),
"Refund is not available yet" );
eosio_assert(request->on_hold, "Request is not hold");
action transfer = action(
permission_level{get_self() ,"active"_n},
"eosio.token"_n,
"transfer"_n,
std::make_tuple(get_self(), user, request->amount, std::string("Here are your tokens"))
);
transfer.send();
db.erase(request);
}
void dep::add_balance(name buyer, name seller, asset value) {
deposits db(_self, buyer.value);
auto dep = db.find(seller.value);
if(dep == db.end()) {
db.emplace(buyer, [&]( auto& a ){
a.seller = seller;
a.amount = value;
});
} else {
db.modify(dep, same_payer, [&]( auto& a ) {
a.amount += value;
});
}
}
extern "C" {
void apply(uint64_t receiver, uint64_t code, uint64_t action) {
auto self = receiver;
if(code == self && action == name("opendeposit").value) {
execute_action(name(receiver), name(code), &dep::opendeposit);
} else if(code == self && action == name("withdraw").value) {
execute_action(name(receiver), name(code), &dep::withdraw);
} else if(code == self && action == name("claim").value) {
execute_action(name(receiver), name(code), &dep::claim);
} else if(code == self && action == name("hold").value) {
execute_action(name(receiver), name(code), &dep::hold);
} else if(code == self && action == name("refund").value) {
execute_action(name(receiver), name(code), &dep::refund);
} else if(code == self && action == name("hold").value) {
execute_action(name(receiver), name(code), &dep::hold);
} else if(code == self && action == name("resolve").value) {
execute_action(name(receiver), name(code), &dep::resolve);
} else if(code == name("eosio.token").value && action == name("transfer").value) {
execute_action(name(receiver), name(code), &dep::transfer);
} else{
eosio_assert(false, (string("Ooops - action not configured: ")+ name(action).to_string()).c_str());
}
}
}
time_point current_time_point() {
const static time_point ct{ microseconds{ static_cast<int64_t>( current_time() ) } };
return ct;
}