-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathEscrowMyEther.sol
373 lines (269 loc) · 15.2 KB
/
EscrowMyEther.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
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
pragma solidity ^0.4.16;
contract EscrowMyEther {
address public owner;
//Each buyer address consist of an array of EscrowStruct
//Used to store buyer's transactions and for buyers to interact with his transactions. (Such as releasing funds to seller)
struct EscrowStruct
{
address buyer; //Person who is making payment
address seller; //Person who will receive funds
address escrow_agent; //Escrow agent to resolve disputes, if any
uint escrow_fee; //Fee charged by escrow
uint amount; //Amount of Ether (in Wei) seller will receive after fees
bool escrow_intervention; //Buyer or Seller can call for Escrow intervention
bool release_approval; //Buyer or Escrow(if escrow_intervention is true) can approve release of funds to seller
bool refund_approval; //Seller or Escrow(if escrow_intervention is true) can approve refund of funds to buyer
bytes32 notes; //Notes for Seller
}
struct TransactionStruct
{
//Links to transaction from buyer
address buyer; //Person who is making payment
uint buyer_nounce; //Nounce of buyer transaction
}
//Database of Buyers. Each buyer then contain an array of his transactions
mapping(address => EscrowStruct[]) public buyerDatabase;
//Database of Seller and Escrow Agent
mapping(address => TransactionStruct[]) public sellerDatabase;
mapping(address => TransactionStruct[]) public escrowDatabase;
//Every address have a Funds bank. All refunds, sales and escrow comissions are sent to this bank. Address owner can withdraw them at any time.
mapping(address => uint) public Funds;
mapping(address => uint) public escrowFee;
//Run once the moment contract is created. Set contract creator
function EscrowMyEther() {
owner = msg.sender;
}
function() payable
{
//LogFundsReceived(msg.sender, msg.value);
}
function setEscrowFee(uint fee) {
//Allowed fee range: 0.1% to 10%, in increments of 0.1%
require (fee >= 1 && fee <= 100);
escrowFee[msg.sender] = fee;
}
function getEscrowFee(address escrowAddress) constant returns (uint) {
return (escrowFee[escrowAddress]);
}
function newEscrow(address sellerAddress, address escrowAddress, bytes32 notes) payable returns (bool) {
require(msg.value > 0 && msg.sender != escrowAddress);
//Store escrow details in memory
EscrowStruct memory currentEscrow;
TransactionStruct memory currentTransaction;
currentEscrow.buyer = msg.sender;
currentEscrow.seller = sellerAddress;
currentEscrow.escrow_agent = escrowAddress;
//Calculates and stores Escrow Fee.
currentEscrow.escrow_fee = getEscrowFee(escrowAddress)*msg.value/1000;
//0.25% dev fee
uint dev_fee = msg.value/400;
Funds[owner] += dev_fee;
//Amount seller receives = Total amount - 0.25% dev fee - Escrow Fee
currentEscrow.amount = msg.value - dev_fee - currentEscrow.escrow_fee;
//These default to false, no need to set them again
/*currentEscrow.escrow_intervention = false;
currentEscrow.release_approval = false;
currentEscrow.refund_approval = false; */
currentEscrow.notes = notes;
//Links this transaction to Seller and Escrow's list of transactions.
currentTransaction.buyer = msg.sender;
currentTransaction.buyer_nounce = buyerDatabase[msg.sender].length;
sellerDatabase[sellerAddress].push(currentTransaction);
escrowDatabase[escrowAddress].push(currentTransaction);
buyerDatabase[msg.sender].push(currentEscrow);
return true;
}
//switcher 0 for Buyer, 1 for Seller, 2 for Escrow
function getNumTransactions(address inputAddress, uint switcher) constant returns (uint)
{
if (switcher == 0) return (buyerDatabase[inputAddress].length);
else if (switcher == 1) return (sellerDatabase[inputAddress].length);
else return (escrowDatabase[inputAddress].length);
}
//switcher 0 for Buyer, 1 for Seller, 2 for Escrow
function getSpecificTransaction(address inputAddress, uint switcher, uint ID) constant returns (address, address, address, uint, bytes32, uint, bytes32)
{
bytes32 status;
EscrowStruct memory currentEscrow;
if (switcher == 0)
{
currentEscrow = buyerDatabase[inputAddress][ID];
status = checkStatus(inputAddress, ID);
}
else if (switcher == 1)
{
currentEscrow = buyerDatabase[sellerDatabase[inputAddress][ID].buyer][sellerDatabase[inputAddress][ID].buyer_nounce];
status = checkStatus(currentEscrow.buyer, sellerDatabase[inputAddress][ID].buyer_nounce);
}
else if (switcher == 2)
{
currentEscrow = buyerDatabase[escrowDatabase[inputAddress][ID].buyer][escrowDatabase[inputAddress][ID].buyer_nounce];
status = checkStatus(currentEscrow.buyer, escrowDatabase[inputAddress][ID].buyer_nounce);
}
return (currentEscrow.buyer, currentEscrow.seller, currentEscrow.escrow_agent, currentEscrow.amount, status, currentEscrow.escrow_fee, currentEscrow.notes);
}
function buyerHistory(address buyerAddress, uint startID, uint numToLoad) constant returns (address[], address[],uint[], bytes32[]){
uint length;
if (buyerDatabase[buyerAddress].length < numToLoad)
length = buyerDatabase[buyerAddress].length;
else
length = numToLoad;
address[] memory sellers = new address[](length);
address[] memory escrow_agents = new address[](length);
uint[] memory amounts = new uint[](length);
bytes32[] memory statuses = new bytes32[](length);
for (uint i = 0; i < length; i++)
{
sellers[i] = (buyerDatabase[buyerAddress][startID + i].seller);
escrow_agents[i] = (buyerDatabase[buyerAddress][startID + i].escrow_agent);
amounts[i] = (buyerDatabase[buyerAddress][startID + i].amount);
statuses[i] = checkStatus(buyerAddress, startID + i);
}
return (sellers, escrow_agents, amounts, statuses);
}
function SellerHistory(address inputAddress, uint startID , uint numToLoad) constant returns (address[], address[], uint[], bytes32[]){
address[] memory buyers = new address[](numToLoad);
address[] memory escrows = new address[](numToLoad);
uint[] memory amounts = new uint[](numToLoad);
bytes32[] memory statuses = new bytes32[](numToLoad);
for (uint i = 0; i < numToLoad; i++)
{
if (i >= sellerDatabase[inputAddress].length)
break;
buyers[i] = sellerDatabase[inputAddress][startID + i].buyer;
escrows[i] = buyerDatabase[buyers[i]][sellerDatabase[inputAddress][startID +i].buyer_nounce].escrow_agent;
amounts[i] = buyerDatabase[buyers[i]][sellerDatabase[inputAddress][startID + i].buyer_nounce].amount;
statuses[i] = checkStatus(buyers[i], sellerDatabase[inputAddress][startID + i].buyer_nounce);
}
return (buyers, escrows, amounts, statuses);
}
function escrowHistory(address inputAddress, uint startID, uint numToLoad) constant returns (address[], address[], uint[], bytes32[]){
address[] memory buyers = new address[](numToLoad);
address[] memory sellers = new address[](numToLoad);
uint[] memory amounts = new uint[](numToLoad);
bytes32[] memory statuses = new bytes32[](numToLoad);
for (uint i = 0; i < numToLoad; i++)
{
if (i >= escrowDatabase[inputAddress].length)
break;
buyers[i] = escrowDatabase[inputAddress][startID + i].buyer;
sellers[i] = buyerDatabase[buyers[i]][escrowDatabase[inputAddress][startID +i].buyer_nounce].seller;
amounts[i] = buyerDatabase[buyers[i]][escrowDatabase[inputAddress][startID + i].buyer_nounce].amount;
statuses[i] = checkStatus(buyers[i], escrowDatabase[inputAddress][startID + i].buyer_nounce);
}
return (buyers, sellers, amounts, statuses);
}
function checkStatus(address buyerAddress, uint nounce) constant returns (bytes32){
bytes32 status = "";
if (buyerDatabase[buyerAddress][nounce].release_approval){
status = "Complete";
} else if (buyerDatabase[buyerAddress][nounce].refund_approval){
status = "Refunded";
} else if (buyerDatabase[buyerAddress][nounce].escrow_intervention){
status = "Pending Escrow Decision";
} else
{
status = "In Progress";
}
return (status);
}
//When transaction is complete, buyer will release funds to seller
//Even if EscrowEscalation is raised, buyer can still approve fund release at any time
function buyerFundRelease(uint ID)
{
require(ID < buyerDatabase[msg.sender].length &&
buyerDatabase[msg.sender][ID].release_approval == false &&
buyerDatabase[msg.sender][ID].refund_approval == false);
//Set release approval to true. Ensure approval for each transaction can only be called once.
buyerDatabase[msg.sender][ID].release_approval = true;
address seller = buyerDatabase[msg.sender][ID].seller;
address escrow_agent = buyerDatabase[msg.sender][ID].escrow_agent;
uint amount = buyerDatabase[msg.sender][ID].amount;
uint escrow_fee = buyerDatabase[msg.sender][ID].escrow_fee;
//Move funds under seller's owership
Funds[seller] += amount;
Funds[escrow_agent] += escrow_fee;
}
//Seller can refund the buyer at any time
function sellerRefund(uint ID)
{
address buyerAddress = sellerDatabase[msg.sender][ID].buyer;
uint buyerID = sellerDatabase[msg.sender][ID].buyer_nounce;
require(
buyerDatabase[buyerAddress][buyerID].release_approval == false &&
buyerDatabase[buyerAddress][buyerID].refund_approval == false);
address escrow_agent = buyerDatabase[buyerAddress][buyerID].escrow_agent;
uint escrow_fee = buyerDatabase[buyerAddress][buyerID].escrow_fee;
uint amount = buyerDatabase[buyerAddress][buyerID].amount;
//Once approved, buyer can invoke WithdrawFunds to claim his refund
buyerDatabase[buyerAddress][buyerID].refund_approval = true;
Funds[buyerAddress] += amount;
Funds[escrow_agent] += escrow_fee;
}
//Either buyer or seller can raise escalation with escrow agent.
//Once escalation is activated, escrow agent can release funds to seller OR make a full refund to buyer
//Switcher = 0 for Buyer, Switcher = 1 for Seller
function EscrowEscalation(uint switcher, uint ID)
{
//To activate EscrowEscalation
//1) Buyer must not have approved fund release.
//2) Seller must not have approved a refund.
//3) EscrowEscalation is being activated for the first time
//There is no difference whether the buyer or seller activates EscrowEscalation.
address buyerAddress;
uint buyerID; //transaction ID of in buyer's history
if (switcher == 0) // Buyer
{
buyerAddress = msg.sender;
buyerID = ID;
} else if (switcher == 1) //Seller
{
buyerAddress = sellerDatabase[msg.sender][ID].buyer;
buyerID = sellerDatabase[msg.sender][ID].buyer_nounce;
}
require(buyerDatabase[buyerAddress][buyerID].escrow_intervention == false &&
buyerDatabase[buyerAddress][buyerID].release_approval == false &&
buyerDatabase[buyerAddress][buyerID].refund_approval == false);
//Activate the ability for Escrow Agent to intervent in this transaction
buyerDatabase[buyerAddress][buyerID].escrow_intervention = true;
}
//ID is the transaction ID from Escrow's history.
//Decision = 0 is for refunding Buyer. Decision = 1 is for releasing funds to Seller
function escrowDecision(uint ID, uint Decision)
{
//Escrow can only make the decision IF
//1) Buyer has not yet approved fund release to seller
//2) Seller has not yet approved a refund to buyer
//3) Escrow Agent has not yet approved fund release to seller AND not approved refund to buyer
//4) Escalation Escalation is activated
address buyerAddress = escrowDatabase[msg.sender][ID].buyer;
uint buyerID = escrowDatabase[msg.sender][ID].buyer_nounce;
require(
buyerDatabase[buyerAddress][buyerID].release_approval == false &&
buyerDatabase[buyerAddress][buyerID].escrow_intervention == true &&
buyerDatabase[buyerAddress][buyerID].refund_approval == false);
uint escrow_fee = buyerDatabase[buyerAddress][buyerID].escrow_fee;
uint amount = buyerDatabase[buyerAddress][buyerID].amount;
if (Decision == 0) //Refund Buyer
{
buyerDatabase[buyerAddress][buyerID].refund_approval = true;
Funds[buyerAddress] += amount;
Funds[msg.sender] += escrow_fee;
} else if (Decision == 1) //Release funds to Seller
{
buyerDatabase[buyerAddress][buyerID].release_approval = true;
Funds[buyerDatabase[buyerAddress][buyerID].seller] += amount;
Funds[msg.sender] += escrow_fee;
}
}
function WithdrawFunds()
{
uint amount = Funds[msg.sender];
Funds[msg.sender] = 0;
if (!msg.sender.send(amount))
Funds[msg.sender] = amount;
}
function CheckBalance(address fromAddress) constant returns (uint){
return (Funds[fromAddress]);
}
}