-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSafeRemotePurchase.etiml
153 lines (131 loc) · 5.28 KB
/
SafeRemotePurchase.etiml
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
pragma etiml ^0.1
(* PW: this way of selling/buying goods is capital heavy because for buying P value of goods both seller and buyer need to commit and lock 2P capital *)
structure Pervasive = struct
fun inc n = n + 1
fun dec n = n - 1
fun nat_inc {n : Nat} (n : nat {n}) = n #+ #1
fun nat_dec {n : Nat | n >= 1} (n : nat {n}) = n #- #1
fun addBy b a = a + b
fun subBy b a = a - b
fun orBy b a = a bit_or b
(* fun waste_time () = 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 *)
fun for_ ['a] {m1 m2: Time} {m1' m2' start : Nat} {eend : Nat | start <= eend} (start : nat {start}, eend : nat {eend}, init : 'a, f : forall {i : Nat | start <= i /\ i < eend} using (m1, m1'), nat {i} * 'a -- m2, m2' --> 'a) return 'a using (m1+m2+3281.0)*$(eend-start)+4012.0, (m1'+m2'+52*32)*(eend-start)+50*32 =
lets
fun loop {i : Nat | start <= i /\ i <= eend} (i : nat {i}, acc : 'a) (* using (m1+m2) * $(eend-i), (m1'+m2') * (eend-i) *) =
ifi i #>= eend then (* waste_time (); *)acc
else
%loop (i #+ #1, %f (i, acc)) using (m1+m2+3281.0) * $(eend-i)+1651.0, (m1'+m2'+52*32) * (eend-i)+16*32
end
in
%loop (start, init)
end
fun require b = if b then (* waste_time (); *)() else (throw using _) end
(* fun unop_ref ['a] {m : Time} {m' : Nat} r (f : 'a -- m, m' --> 'a) = r := f !r *)
val ether = 1000000000000000000
val minute = 60
val hour = 60 * minute
val day = 24 * hour
end
contract Purchase = struct
public state value : cell uint
public state seller : cell address
public state buyer : cell address
type State = int
val Created = 0
val Locked = 1
val Inactive = 2
public state mState : cell int
public state sellerCanWithdraw : cell uint
public state buyerCanWithdraw : cell uint
(* Ensure that `msg.value` is an even number. *)
(* Division will truncate if it is an odd number. *)
(* Check via multiplication that it wasn't an odd number. *)
fun constructor(value_) public payable =
seller ::= msg.sender;
value ::= (* msg.value *)value_ / 2;
require((2 * !!value) == (* msg.value *)value_)
(* modifier condition(bool _condition) { *)
(* require(_condition); *)
(* _; *)
(* } *)
fun onlyBuyer() =
require(
msg.sender == !!buyer
)
fun onlySeller() =
require(
msg.sender == !!seller
)
fun inState(_state : State) =
require(
!!mState == _state
)
(* event Aborted(); *)
(* event PurchaseConfirmed(); *)
(* event ItemReceived(); *)
(*/ Abort the purchase and reclaim the ether. *)
(*/ Can only be called by the seller before *)
(*/ the contract is locked. *)
fun abort() public =
onlySeller ();
inState(Created);
(* emit Aborted(); *)
mState ::= Inactive;
(* seller.transfer(this_balance) *)
sellerCanWithdraw ::= this_balance
(*/ Confirm the purchase as buyer. *)
(*/ Transaction has to include `2 * value` ether. *)
(*/ The ether will be locked until confirmReceived *)
(*/ is called. *)
fun confirmPurchase()
public
payable =
inState(Created);
require(msg.value == (2 * !!value));
(* emit PurchaseConfirmed(); *)
buyer ::= msg.sender;
mState ::= Locked
(* PW: If the seller doesn't ship the good after this point, both sides lose 2*value money. *)
(* If the buy receives the good but doesn't call confirmReceived(), seller loses 3*value money and buy loses value money. *)
(* ;debug_log (!!mState) *)
(*/ Confirm that you (the buyer) received the item. *)
(*/ This will release the locked ether. *)
fun confirmReceived() public =
onlyBuyer ();
inState(Locked);
(* emit ItemReceived(); *)
(* It is important to change the state first because *)
(* otherwise, the contracts called using `send` below *)
(* can call in again here. *)
mState ::= Inactive;
(* NOTE: This actually allows both the buyer and the seller to *)
(* block the refund - the withdraw pattern should be used. *)
(* buyer.transfer(value); *)
(* seller.transfer(address(this).balance); *)
buyerCanWithdraw ::= !!value;
(* sellerCanWithdraw ::= this_balance - !!value *)
sellerCanWithdraw ::= 3 * !!value
(* ;debug_log (!!mState, !!buyerCanWithdraw, !!sellerCanWithdraw) *)
fun buyerWithdraw() public =
onlyBuyer ();
inState(Inactive);
let value = !!buyerCanWithdraw;
buyerCanWithdraw ::= 0
(* buyer.transfer(value) *)
fun sellerWithdraw() public =
onlySeller ();
inState(Inactive);
let value = sellerCanWithdraw;
sellerCanWithdraw ::= 0
(* seller.transfer(value) *)
val () = constructor 0x20
(* (* val () = debug_log (!!mState) *) *)
(* val () = confirmPurchase() *)
(* (* val () = debug_log (!!mState) *) *)
(* (* val () = confirmReceived() *) *)
(* (* val () = debug_log (!!mState, !!buyerCanWithdraw, !!sellerCanWithdraw) *) *)
(* (* val _ = dispatch { *) *)
(* (* confirmPurchase = confirmPurchase, *) *)
(* (* confirmReceived = confirmReceived, *) *)
(* (* } *) *)
end