mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			108 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
.. index:: purchase, remote purchase, escrow
 | 
						|
 | 
						|
********************
 | 
						|
Safe Remote Purchase
 | 
						|
********************
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
    pragma solidity >=0.4.22 <0.7.0;
 | 
						|
 | 
						|
    contract Purchase {
 | 
						|
        uint public value;
 | 
						|
        address payable public seller;
 | 
						|
        address payable public buyer;
 | 
						|
        enum State { Created, Locked, Inactive }
 | 
						|
        // The state variable has a default value of the first member, `State.created`
 | 
						|
        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);
 | 
						|
        }
 | 
						|
    } |