Merge pull request #7780 from ethereum/explainRemotePurchase

[DOCS] Add more explanation and withdraw pattern.
This commit is contained in:
chriseth 2019-11-26 13:59:31 +01:00 committed by GitHub
commit 24072de4e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,6 +4,25 @@
Safe Remote Purchase Safe Remote Purchase
******************** ********************
Purchasing goods remotely currently requires multiple parties that need to trust each other.
The simplest configuration involves a seller and a buyer. The buyer would like to receive
an item from the seller and the seller would like to get money (or an equivalent)
in return. The problematic part is the shipment here: There is no way to determine for
sure that the item arrived at the buyer.
There are multiple ways to solve this problem, but all fall short in one or the other way.
In the following example, both parties have to put twice the value of the item into the
contract as escrow. As soon as this happened, the money will stay locked inside
the contract until the buyer confirms that they received the item. After that,
the buyer is returned the value (half of their deposit) and the seller gets three
times the value (their deposit plus the value). The idea behind
this is that both parties have an incentive to resolve the situation or otherwise
their money is locked forever.
This contract of course does not solve the problem, but gives an overview of how
you can use state machine-like constructs inside a contract.
:: ::
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.7.0;
@ -12,7 +31,7 @@ Safe Remote Purchase
uint public value; uint public value;
address payable public seller; address payable public seller;
address payable public buyer; address payable public buyer;
enum State { Created, Locked, Inactive } enum State { Created, Locked, Release, Inactive }
// The state variable has a default value of the first member, `State.created` // The state variable has a default value of the first member, `State.created`
State public state; State public state;
@ -57,6 +76,7 @@ Safe Remote Purchase
event Aborted(); event Aborted();
event PurchaseConfirmed(); event PurchaseConfirmed();
event ItemReceived(); event ItemReceived();
event SellerRefunded();
/// Abort the purchase and reclaim the ether. /// Abort the purchase and reclaim the ether.
/// Can only be called by the seller before /// Can only be called by the seller before
@ -68,6 +88,10 @@ Safe Remote Purchase
{ {
emit Aborted(); emit Aborted();
state = State.Inactive; state = State.Inactive;
// We use transfer here directly. It is
// reentrancy-safe, because it is the
// last call in this function and we
// already changed the state.
seller.transfer(address(this).balance); seller.transfer(address(this).balance);
} }
@ -97,12 +121,24 @@ Safe Remote Purchase
// It is important to change the state first because // It is important to change the state first because
// otherwise, the contracts called using `send` below // otherwise, the contracts called using `send` below
// can call in again here. // can call in again here.
state = State.Inactive; state = State.Release;
// NOTE: This actually allows both the buyer and the seller to
// block the refund - the withdraw pattern should be used.
buyer.transfer(value); buyer.transfer(value);
seller.transfer(address(this).balance); }
/// This function refunds the seller, i.e.
/// pays back the locked funds of the seller.
function refundSeller()
public
onlySeller
inState(State.Release)
{
emit SellerRefunded();
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
seller.transfer(3 * value);
} }
} }