aboutsummaryrefslogtreecommitdiffstats
path: root/docs/examples/safe-remote.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/examples/safe-remote.rst')
-rw-r--r--docs/examples/safe-remote.rst107
1 files changed, 107 insertions, 0 deletions
diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst
new file mode 100644
index 00000000..cfc63a24
--- /dev/null
+++ b/docs/examples/safe-remote.rst
@@ -0,0 +1,107 @@
+.. index:: purchase, remote purchase, escrow
+
+********************
+Safe Remote Purchase
+********************
+
+::
+
+ pragma solidity >=0.4.22 <0.6.0;
+
+ contract Purchase {
+ uint public value;
+ address payable public seller;
+ address payable public buyer;
+ enum State { Created, Locked, Inactive }
+ State public state;
+
+ // 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.
+ constructor() public payable {
+ seller = msg.sender;
+ value = msg.value / 2;
+ require((2 * value) == msg.value, "Value has to be even.");
+ }
+
+ modifier condition(bool _condition) {
+ require(_condition);
+ _;
+ }
+
+ modifier onlyBuyer() {
+ require(
+ msg.sender == buyer,
+ "Only buyer can call this."
+ );
+ _;
+ }
+
+ modifier onlySeller() {
+ require(
+ msg.sender == seller,
+ "Only seller can call this."
+ );
+ _;
+ }
+
+ modifier inState(State _state) {
+ require(
+ state == _state,
+ "Invalid 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.
+ function abort()
+ public
+ onlySeller
+ inState(State.Created)
+ {
+ emit Aborted();
+ state = State.Inactive;
+ seller.transfer(address(this).balance);
+ }
+
+ /// Confirm the purchase as buyer.
+ /// Transaction has to include `2 * value` ether.
+ /// The ether will be locked until confirmReceived
+ /// is called.
+ function confirmPurchase()
+ public
+ inState(State.Created)
+ condition(msg.value == (2 * value))
+ payable
+ {
+ emit PurchaseConfirmed();
+ buyer = msg.sender;
+ state = State.Locked;
+ }
+
+ /// Confirm that you (the buyer) received the item.
+ /// This will release the locked ether.
+ function confirmReceived()
+ public
+ onlyBuyer
+ inState(State.Locked)
+ {
+ emit ItemReceived();
+ // It is important to change the state first because
+ // otherwise, the contracts called using `send` below
+ // can call in again here.
+ state = State.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);
+ }
+ } \ No newline at end of file