diff options
Diffstat (limited to 'docs/examples/safe-remote.rst')
-rw-r--r-- | docs/examples/safe-remote.rst | 107 |
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 |