-
Notifications
You must be signed in to change notification settings - Fork 1
/
procurement-simple.sol
179 lines (142 loc) · 5.34 KB
/
procurement-simple.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
pragma solidity ^0.4.16;
contract Procurement {
enum ContractStatus { Proposed, Open, Closed }
enum OrderStatus { Ordered, Delivered }
struct Order {
bool exists;
uint cost;
OrderStatus status;
uint deliveryTimeDue;
}
// Addresses of buyer and seller in contract
address public seller;
address public buyer;
// Contract parameters
uint public minimumItemsToBeOrdered;
uint public maximumItemsToBeOrdered;
uint public costPerUnit;
uint public performanceGuarantee;
uint public endOfContractTimestamp;
// Contract status
ContractStatus public contractStatus;
uint8 public itemsOrderedCount;
uint public moneyLeftInContract;
// Orders
mapping (uint8 => Order) public orders;
uint8 public pendingOrderCount;
uint public pendingOrderCost;
uint public lateOrdersCount;
uint public inTimeOrdersCount;
modifier bySeller {
require(msg.sender == seller);
_;
}
modifier byBuyer {
require(msg.sender == buyer);
_;
}
function Procurement(
address _seller, address _buyer,
uint _minimumItemsToBeOrdered, uint _maximumItemsToBeOrdered,
uint _costPerUnit,
uint _performanceGuarantee,
uint _contractDuration
) public payable
{
// The minimum order size must be put in escrow at signing time
require(msg.value >= _costPerUnit * _minimumItemsToBeOrdered);
// Set the contract parameters
seller = _seller;
buyer = _buyer;
minimumItemsToBeOrdered = _minimumItemsToBeOrdered;
maximumItemsToBeOrdered = _maximumItemsToBeOrdered;
costPerUnit = _costPerUnit;
endOfContractTimestamp = now + _contractDuration;
performanceGuarantee = _performanceGuarantee;
// Contract status
contractStatus = ContractStatus.Proposed;
itemsOrderedCount = 0;
moneyLeftInContract = msg.value;
// Initialise orders
pendingOrderCount = 0;
pendingOrderCost = 0;
lateOrdersCount = 0;
inTimeOrdersCount = 0;
}
function acceptContract() public payable bySeller {
require(msg.value >= performanceGuarantee);
contractStatus = ContractStatus.Open;
}
function createOrder(
uint8 _orderNumber,
uint8 _orderSize,
uint _orderDeliveryTimeLeft
) public payable byBuyer
{
// Order does not already exist
require(!orders[_orderNumber].exists);
// Number of items ordered does not exceed maximum
require(itemsOrderedCount + _orderSize <= maximumItemsToBeOrdered);
// Order delivery deadline will not be too late
require(now + _orderDeliveryTimeLeft <= endOfContractTimestamp);
// Ensure there is enough money left in the contract to pay for the order
uint orderCost = _orderSize * costPerUnit;
moneyLeftInContract += msg.value;
require(orderCost <= moneyLeftInContract);
moneyLeftInContract -= orderCost;
// Update number of items ordered
itemsOrderedCount += _orderSize;
// Update contract status
pendingOrderCount++;
pendingOrderCost += orderCost;
// Record the order
orders[_orderNumber] = Order(true, orderCost, OrderStatus.Ordered, now+_orderDeliveryTimeLeft);
}
function deliveryMade(
uint8 _orderNumber
) public byBuyer
{
Order memory order = orders[_orderNumber];
// Ensure that the order exists and has not yet been delivered
require(order.exists && order.status == OrderStatus.Ordered);
// Order state update
order.status = OrderStatus.Delivered;
// Contract state update
if (order.deliveryTimeDue < now) {
lateOrdersCount++;
} else {
inTimeOrdersCount++;
}
pendingOrderCount--;
pendingOrderCost -= order.cost;
// Pay the seller
seller.transfer(order.cost);
}
function terminateContract() public {
// Can only be done by the seller or buyer
require(msg.sender == seller || msg.sender == buyer);
// Can only be closed after the contract time frame ended
require(now > endOfContractTimestamp);
if (msg.sender == seller) {
// Can only be closed by seller if there are no pending orders
require(pendingOrderCount == 0);
}
if (pendingOrderCount > 0) {
// If there are any undelivered orders, return their cost to the buyer
buyer.transfer(pendingOrderCost);
} else {
// If there are no undelivered orders, and not enough orders were made (less
// than minimum) the seller gets money for the unordered items
if (itemsOrderedCount < minimumItemsToBeOrdered) {
seller.transfer((itemsOrderedCount-minimumItemsToBeOrdered)*costPerUnit);
}
}
// If there are any pending orders or 25%+ of the orders were delivered late
// the buyer gets the performance guarantee, otherwise it is returned to the seller
if ((pendingOrderCount > 0) || (lateOrdersCount * 3 >= inTimeOrdersCount)) {
buyer.transfer(performanceGuarantee);
} else {
seller.transfer(performanceGuarantee);
}
}
}