mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			107 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			107 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. 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);
 | |
|         }
 | |
|     } |