-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFish Trade.sol
362 lines (259 loc) · 15.1 KB
/
Fish Trade.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
pragma solidity 0.5.1; /*
___________________________________________________________________
_ _ ______
| | / / /
--|-/|-/-----__---/----__----__---_--_----__-------/-------__------
|/ |/ /___) / / ' / ) / / ) /___) / / )
__/__|____(___ _/___(___ _(___/_/_/__/_(___ _____/______(___/__o_o_
███████╗██╗███████╗██╗ ██╗ ████████╗██████╗ █████╗ ██████╗ ███████╗
██╔════╝██║██╔════╝██║ ██║ ╚══██╔══╝██╔══██╗██╔══██╗██╔══██╗██╔════╝
█████╗ ██║███████╗███████║ ██║ ██████╔╝███████║██║ ██║█████╗
██╔══╝ ██║╚════██║██╔══██║ ██║ ██╔══██╗██╔══██║██║ ██║██╔══╝
██║ ██║███████║██║ ██║ ██║ ██║ ██║██║ ██║██████╔╝███████╗
╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝
THE LOGIC:
A buys 5 kg fresh fish of good quality from B for 12 Ether.
B sends the order, as soon as the escrow receives the money from A. The fish must be delivered the next day (1st of February 2019) to A.
If - 5 KG of the fish; In a good quality; Is Delivered on time -> B will get the money from the escrow.
MAIN COMPONENTS
=> Ownership of contract
=> Oracle system
=> Main Fish Trade
=> Escrow system
POSSIBLE EVENTS:
If the fish is not of good quality: A has to report to B that the fish is not of good quality.
If A reports that the fish is not of good quality in 2 hours, A will get his money back from the escrow.
If the fish is not delivered in time, that means not on the 1st of February 2019:
If the fish does not get delivered to A in time, that means not on the 1st of February 2019, A will get his money back from the escrow.
If A doesn’t accept the delivery: If A does not accept the right delivery,
(The delivery is right when: - The delivery of 5kg fish - In Good Quality - On the 1st of February 2019. - At A’s place. )
B still gets the money from the escrow.
The data about fish is coming from the Oracle, which handled by the Oracle Contract
*/
//*******************************************************************//
//------------------ Contract to Manage Ownership -------------------//
//*******************************************************************//
contract owned {
address public owner;
constructor () public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}
//*******************************************************************//
//--------------------- Contract for Oracle -------------------------//
//*******************************************************************//
contract Oracle is owned {
/* Public variables of Oracle */
bool fishQuality;
uint256 fishQuantity;
uint256 deliveryDate;
/* Struct which holds all the requests received by the oracle for the logging purpose */
struct Request {
bool fishQuality;
uint256 fishQuantity;
uint256 deliveryDate;
}
/* This array will hold all the structs */
Request[] requests;
/**
* @dev This event will be emitted when there is need to update the Fish data
* @dev Which will cause oracle script, who listen for this event, to respond
* @dev Which will then call reply() function with valid parameters which will be unpdate in smart contract
* @dev parameter will be requet ID used to validate the each request
*/
event NewRequest();
/**
* @dev This function will be called to request data from outside source
* @dev It will emit event for oracles
*
*
*/
function queryOracle() public {
emit NewRequest();
}
/**
* @dev This function will be called by the Oracle to supply the data to update the smart contract
* @dev To check validity of the response, we will be making sure the caller must be owner
*
* @param fishQualityNew The quality of the fish, data provided by Oracle
* @param fishQuantityNew The quantity of the fish, data provided by Oracle
* @param deliveryDateNew The delivery date of the fish, data provided by Oracle
*/
function responseFromOracle(bool fishQualityNew, uint256 fishQuantityNew, uint256 deliveryDateNew) public onlyOwner {
//validate the incoming data
require(fishQuantityNew > 0 && fishQuantityNew <= 5);
require(deliveryDateNew > 0 && deliveryDateNew <= 999999999999999999); //this is to prevent over flow or under flow
// update the fish data
fishQuality = fishQualityNew;
fishQuantity = fishQuantityNew;
deliveryDate = deliveryDateNew;
// adding those data into an struct and array for the logging purpose
requests.push(Request(fishQualityNew, fishQuantityNew, deliveryDateNew));
}
}
//*********************************************************************//
//------------------ FishTrade Contract Starts Here -------------------//
//*********************************************************************//
contract FishTrade is owned, Oracle {
/***********************************/
/* Use case of Fish Trade */
/***********************************/
/**
* @dev Payable function. As for our case, it will only accept 12 Ether
* @dev Buyer (person A) will send money to escrow using this function
* @dev Buyer also have to specify the Seller address
* @dev The reason to specify Seller is because it will tag to this trade.
* @dev So, when buyer will release escrow, then fund will be transfered to seller
*
* @param _seller The address of seller
*/
function buyerDeposit(address _seller) payable public {
require(msg.value == 12 ether, 'Ether value must be 12 Ether');
payToEscrow(_seller);
}
/**
* @dev This function must be called by seller to check if buyer has deposited 12 ether
*
* @param _buyer The address of the buyer
* @param _seller The address of the seller
* @param timestamp The timestamp of the trade.
* @param ethAmountWEI The amount of ether in WEI
*
* @return bool true or false, whether buyer paid or not
*/
function paymentConfirmationCheck(address _buyer, address _seller, uint256 timestamp, uint256 ethAmountWEI) public view returns(bool paid) {
/* Calculating the hash from given parameters */
bytes32 hash = keccak256(abi.encodePacked(this, _buyer, _seller, timestamp, ethAmountWEI));
/* For our case, it should be 12 Ether */
require( escrowBalance[_buyer][_seller][hash] == 12 ether , 'Transaction amount is zero. Most probably the input parameters are invalid.');
return paid;
}
/**
* @dev This function called by buyer (Person A) when the delivery arrives
* @dev The data of fish coming from the Oracle
*
* @dev If fish is not in good quality as well as delivery is not on time, then refund buyer
* @dev If delivery is right and buyer accepts it, then release escrow and payment sent to seller
*
* @param _seller Address of seller
* @param ethAmountWEI The amount of ether
* @param orderTimestamp The timestamp of order
*
*/
function fishDelivered( address payable _seller, uint256 ethAmountWEI, uint256 orderTimestamp ) public {
/* Getting fish data updates from Oracle */
queryOracle();
/* If A reports that the fish is not of good quality in 2 hours, A will get his money back from the escrow. */
if(fishQuality == false){ //false means no good!
refundBuyer(_seller, ethAmountWEI, orderTimestamp);
}
/* If the fish does not get delivered to A in time, that means not on the 1st of February 2019, A will get his money back from the escrow. */
if(deliveryDate != 1548979200){ //1548979200 = 01 Feb 2019 00:00:00
refundBuyer(_seller, ethAmountWEI, orderTimestamp);
}
/* If delivery is right, and buyer (person A) accept it then payment sent to seller (Person B) */
if(isRightDelivery()){
releaseEscrow(msg.sender, address(_seller), orderTimestamp, ethAmountWEI);
}
}
/**
* @dev This function called by seller (Person B) when buyer (Person A) does not accept the delivery
* @dev Escrow is relased and seller (Person B) gets the money
*
* @param _buyer Address of buyer
* @param ethAmountWEI The amount of ether
* @param orderTimestamp The timestamp of order
*/
function deliveryNotAccepted(address _buyer, uint256 ethAmountWEI, uint256 orderTimestamp) public {
releaseEscrow(_buyer, msg.sender, orderTimestamp, ethAmountWEI);
}
/**
* @dev This function checks whether delivery is right or not
* @dev It considers the data updated by the Oracle
*
* @return bool returns whether this is right delivery or not
*/
function isRightDelivery() internal view returns(bool) {
/* Fish quantity must be true=good */
if(fishQuality != true){
return false;
}
/* Fish quantity must be 5 Kg */
else if(fishQuantity != 5){
return false;
}
/* Delivery date must be 01 Feb 2019 */
else if(deliveryDate != 1548979200){ //01 Feb 2019 00:00:00
return false;
}
/* The deivery is right then return true */
else{
return true;
}
}
/*********************************/
/* Section for Escrow */
/*********************************/
/* Mapping which will map buyer address with seller, unique transaction hash and their ETH amount */
mapping (address => mapping (address => mapping (bytes32 => uint256))) public escrowBalance;
/* Events which logs all the transactions */
event EscrowDeposit(address buyer, address seller, uint256 ethAmount, uint256 timestamp, bytes32 txnHash );
event ReleaseEscrow(address buyer, address seller, uint256 ethAmount, uint256 timestamp, bytes32 txnHash );
/**
* @dev Buyer will send money to escrow using this function
* @dev To make every transaction unique, we will create a simple transaction ID
*
* @param _seller The address of the seller, which should be specified by buyer
* @return success Whether payment to escrow was a success or not
*/
function payToEscrow(address _seller) internal {
/* Validating the user input */
require(msg.value > 0, 'Ether amount must be more than zero');
require(_seller != address(0), 'Seller address is invalid');
/* Calculating the transaction has from the user inputf */
bytes32 hash = keccak256(abi.encodePacked(this, msg.sender, _seller, now, msg.value));
/* Adds the amount in Escrow balance mappubg */
escrowBalance[msg.sender][_seller][hash] = msg.value;
/* Emitting the event */
emit EscrowDeposit(msg.sender, _seller, msg.value, now, hash );
}
/**
* Once Fishes received, then buyer will release escrow
* It will calculate the transaction hash again from the input parameter to verify the transaction
*
* @param _buyer The address of the buyer
* @param _seller The address of the seller
* @param ethAmountWEI The amount of ether in WEI
* @param timestamp The timestamp of the trade.
*
*/
function releaseEscrow(address _buyer, address payable _seller, uint256 timestamp, uint256 ethAmountWEI) internal {
/* Calculating the hash from given parameters */
bytes32 hash = keccak256(abi.encodePacked(this, _buyer, _seller, timestamp, ethAmountWEI));
require( escrowBalance[_buyer][_seller][hash] > 0 , 'Transaction amount is zero. Most probably the input parameters are invalid.');
/* Setting Escrow amount to zerp */
escrowBalance[_buyer][_seller][hash] = 0;
/* Transferring the ETH to seller */
address(_seller).transfer(ethAmountWEI);
/* Emitting the event */
emit ReleaseEscrow(msg.sender, _seller, ethAmountWEI, timestamp, hash );
}
function refundBuyer(address _seller, uint256 ethAmountWEI, uint256 timestamp) public {
/* Calculating the hash from given parameters */
bytes32 hash = keccak256(abi.encodePacked(this, msg.sender, _seller, timestamp, ethAmountWEI));
require( escrowBalance[msg.sender][_seller][hash] > 0 , 'Transaction amount is zero. Most probably the input parameters are invalid.');
/* Setting Escrow amount to zerp */
escrowBalance[msg.sender][_seller][hash] = 0;
/* Transferring the ETH to seller */
address(msg.sender).transfer(ethAmountWEI);
/* Emitting the event */
emit ReleaseEscrow(msg.sender, _seller, ethAmountWEI, timestamp, hash );
}
}