09-17-2022, 09:33 AM
Solidity Example – Safe Remote Purchase
5/5 – (1 vote)
![[Image: lyteCache.php?origThumbUrl=https%3A%2F%2...efault.jpg]](https://blog.finxter.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=https%3A%2F%2Fi.ytimg.com%2Fvi%2FSZ-agoVo67A%2Fhqdefault.jpg)
This article continues on the Solidity Smart Contract Examples series, which implements a simple, but the useful process of safe remote purchase.
Here, we’re walking through an example of a blind auction (docshttps://docs.soliditylang.org/en/v0.8.15...e-purchase).
Smart Contract – Safe Remote Purchase
Code breakdown and analysis
The state variables for recording the value, seller, and buyer addresses.
For the first time, we’re introducing the data structure that symbolically defines the four possible states of our contract. The states are internally indexed from to .
The variable state keeps track of the current state. Our contract starts by default in the created state and can transition to the Locked, Release, and Inactive state.
The modifier guards a function against executing without previously satisfying the condition, i.e. an expression given alongside the function definition.
The error definitions are used with the appropriate, equally-named modifiers.
The modifier guards a function against executing when the function caller is not the buyer.
The modifier guards a function against executing when the function caller differs from the seller.
The modifier guards a function against executing when the contract state differs from the required .
The events that the contract emits to acknowledge the functions, , , and were executed.
The constructor is declared as, meaning that the contract deployment (synonyms creation, instantiation) requires sending a value () with the contract-creating transaction.
The state variable is set to address, cast (converted) to payable.
The value state variable is set to half the, because both the seller and the buyer have to put twice the value of the item being sold/bought into the contract as an escrow agreement.
Info: “Escrow is a legal arrangement in which a third party temporarily holds money or property until a particular condition has been met (such as the fulfillment of a purchase agreement).” (source)
In our case, our escrow is our smart contract.
If the value is not equally divided, i.e. the is not an even number, the function will terminate. Since the seller will always
Aborting the remote safe purchase is allowed only in the state and only by the seller.
The keyword makes the function callable only by other accounts / smart contracts. From the business perspective, only the seller can call the function and only before the buyer decides to purchase, i.e. before the contract enters the state.
Emits the event, the contract state transitions to inactive, and the balance is transferred to the seller.
Note: “Prior to version 0.5.0, Solidity allowed address members to be accessed by a contract instance, for example, this.balance. This is now forbidden and an explicit conversion to address must be done: address(this).balance.” (docs).
In other words, this keyword lets us access the contract’s inherited members.
Every contract inherits its members from the address type and can access these members via (docs).
The function is available for execution only in the state.
It enforces the rule that a must be twice the value of the purchase.
The function is also declared as , meaning the caller, i.e. the buyer has to send the currency () with the function call.
The event is emitted to mark the purchase confirmation.
The value is cast to payable and assigned to the buyer variable.
Info: Addresses are non-payable by design to prevent accidental payments; that’s why we have to cast an address to a payable before being able to transfer a payment.
The state is set to as seller and buyer entered the contract, i.e., our digital version of an escrow agreement.
The function is available for execution only in the state, and only to the buyer.
Since the buyer deposited twice the value amount and withdrew only a single value amount, the second value amount remains on the contract balance with the seller’s deposit.
Emits the event.
Changes the state to Release.
Transfers the deposit to the buyer.
The function is available for execution only in the state, and only to the seller.
Since the seller deposited twice the value amount and earned a single value amount from the purchase, the contract transfers three value amounts from the contract balance to the seller.
Emits the event.
Changes the state to.
Transfers the deposit of two value amounts and the one earned value amount to the seller.
Our smart contract example of a safe remote purchase is a nice and simple example that demonstrates how a purchase may be conducted on the Ethereum blockchain network.
The safe remote purchase example shows two parties, a seller and a buyer, who both enter a trading relationship with their deposits to the contract balance.
Each deposit amounts to twice the value of the purchase, meaning that the contract balance will hold four times the purchase value at its highest point, i.e. in the state.
The height of deposits is intended to stimulate the resolution of any possible disputes between the parties, because otherwise, their deposits will stay locked and unavailable in the contract balance.
When the buyer confirms that he received the goods he purchased, the contract will transition to the state, and the purchase value will be released to the buyer.
The seller can now withdraw his earned purchase value with the deposit, the contract balance drops to 0 Wei, the contract transitions to the state, and the safe remote purchase concludes with execution.
The Contract Arguments
This section contains additional information for running the contract. We should expect that our example accounts may change with each refresh/reload of Remix.
Our contract creation argument is the deposit (twice the purchase value). We’ll assume the purchase value to be 5 Wei, making the contract creation argument very simple:
Contract Test Scenario
Conclusion
We continued our smart contract example series with this article that implements a safe remote purchase.
First, we laid out clean source code (without any comments) for readability purposes.
Second, we dissected the code, analyzed it, and explained each possibly non-trivial segment.
5/5 – (1 vote)
This article continues on the Solidity Smart Contract Examples series, which implements a simple, but the useful process of safe remote purchase.
Here, we’re walking through an example of a blind auction (docshttps://docs.soliditylang.org/en/v0.8.15...e-purchase).
- We’ll first lay out the entire smart contract example without the comments for readability and development purposes.
- Then we’ll dissect it part by part, analyze it and explain it.
- Following this path, we’ll get a hands-on experience with smart contracts, as well as good practices in coding, understanding, and debugging smart contracts.
Smart Contract – Safe Remote Purchase
Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract Purchase { uint public value; address payable public seller; address payable public buyer; enum State { Created, Locked, Release, Inactive } State public state; modifier condition(bool condition_) { require(condition_); _; } error OnlyBuyer(); error OnlySeller(); error InvalidState(); error ValueNotEven(); modifier onlyBuyer() { if (msg.sender != buyer) revert OnlyBuyer(); _; } modifier onlySeller() { if (msg.sender != seller) revert OnlySeller(); _; } modifier inState(State state_) { if (state != state_) revert InvalidState(); _; } event Aborted(); event PurchaseConfirmed(); event ItemReceived(); event SellerRefunded(); constructor() payable { seller = payable(msg.sender); value = msg.value / 2; if ((2 * value) != msg.value) revert ValueNotEven(); } function abort() external onlySeller inState(State.Created) { emit Aborted(); state = State.Inactive; seller.transfer(address(this).balance); } function confirmPurchase() external inState(State.Created) condition(msg.value == (2 * value)) payable { emit PurchaseConfirmed(); buyer = payable(msg.sender); state = State.Locked; } function confirmReceived() external onlyBuyer inState(State.Locked) { emit ItemReceived(); state = State.Release; buyer.transfer(value); } function refundSeller() external onlySeller inState(State.Release) { emit SellerRefunded(); state = State.Inactive; seller.transfer(3 * value); }
}Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract Purchase {The state variables for recording the value, seller, and buyer addresses.
Code:
uint public value; address payable public seller; address payable public buyer;For the first time, we’re introducing the
Code:
enumCode:
enum_length - 1Code:
enum State { Created, Locked, Release, Inactive }The variable state keeps track of the current state. Our contract starts by default in the created state and can transition to the Locked, Release, and Inactive state.
Code:
State public state;The
Code:
conditionCode:
modifier condition(bool condition_) { require(condition_); _; }The error definitions are used with the appropriate, equally-named modifiers.
Code:
error OnlyBuyer(); error OnlySeller(); error InvalidState(); error ValueNotEven();The
Code:
onlyBuyerCode:
modifier onlyBuyer() { if (msg.sender != buyer) revert OnlyBuyer(); _; }The
Code:
onlySellerCode:
modifier onlySeller() { if (msg.sender != seller) revert OnlySeller(); _; }The
Code:
inStateCode:
state_Code:
modifier inState(State state_) { if (state != state_) revert InvalidState(); _; }The events that the contract emits to acknowledge the functions
Code:
abort()Code:
confirmPurchase()Code:
confirmReceived()Code:
refundSeller()Code:
event Aborted(); event PurchaseConfirmed(); event ItemReceived(); event SellerRefunded();The constructor is declared as
Code:
payableCode:
msg.valueCode:
constructor() payable {The
Code:
sellerCode:
msg.senderCode:
seller = payable(msg.sender);The value state variable is set to half the
Code:
msg.value
Info: “Escrow is a legal arrangement in which a third party temporarily holds money or property until a particular condition has been met (such as the fulfillment of a purchase agreement).” (source)In our case, our escrow is our smart contract.
Code:
value = msg.value / 2;If the value is not equally divided, i.e. the
Code:
msg.valueCode:
if ((2 * value) != msg.value) revert ValueNotEven(); }Aborting the remote safe purchase is allowed only in the
Code:
CreatedThe
Code:
externalCode:
abort()Code:
LockedCode:
function abort() external onlySeller inState(State.Created) {Emits the
Code:
AbortedCode:
emit Aborted(); state = State.Inactive;
Note: “Prior to version 0.5.0, Solidity allowed address members to be accessed by a contract instance, for example, this.balance. This is now forbidden and an explicit conversion to address must be done: address(this).balance.” (docs).In other words, this keyword lets us access the contract’s inherited members.
Every contract inherits its members from the address type and can access these members via
Code:
address(this).<a member>Code:
seller.transfer(address(this).balance); }The
Code:
confirmPurchase()Code:
CreatedIt enforces the rule that a
Code:
msg.valueThe
Code:
confirmPurchase()Code:
payableCode:
msg.valueCode:
function confirmPurchase() external inState(State.Created) condition(msg.value == (2 * value)) payable {The event
Code:
PurchaseConfirmed()Code:
emit PurchaseConfirmed();The
Code:
msg.sender
Info: Addresses are non-payable by design to prevent accidental payments; that’s why we have to cast an address to a payable before being able to transfer a payment.Code:
buyer = payable(msg.sender);The state is set to
Code:
LockedCode:
state = State.Locked; }The
Code:
confirmReceived()Code:
LockedSince the buyer deposited twice the value amount and withdrew only a single value amount, the second value amount remains on the contract balance with the seller’s deposit.
Code:
function confirmReceived() external onlyBuyer inState(State.Locked) {Emits the
Code:
ItemReceived()Code:
emit ItemReceived();Changes the state to Release.
Code:
state = State.Release;Transfers the deposit to the buyer.
Code:
buyer.transfer(value); }The
Code:
refundSeller()Code:
ReleaseSince the seller deposited twice the value amount and earned a single value amount from the purchase, the contract transfers three value amounts from the contract balance to the seller.
Code:
function refundSeller() external onlySeller inState(State.Release) {Emits the
Code:
SellerRefunded()Code:
emit SellerRefunded();Changes the state to
Code:
InactiveCode:
state = State.Inactive;Transfers the deposit of two value amounts and the one earned value amount to the seller.
Code:
seller.transfer(3 * value); }
}Our smart contract example of a safe remote purchase is a nice and simple example that demonstrates how a purchase may be conducted on the Ethereum blockchain network.
The safe remote purchase example shows two parties, a seller and a buyer, who both enter a trading relationship with their deposits to the contract balance.
Each deposit amounts to twice the value of the purchase, meaning that the contract balance will hold four times the purchase value at its highest point, i.e. in the
Code:
LockedThe height of deposits is intended to stimulate the resolution of any possible disputes between the parties, because otherwise, their deposits will stay locked and unavailable in the contract balance.
When the buyer confirms that he received the goods he purchased, the contract will transition to the
Code:
ReleaseThe seller can now withdraw his earned purchase value with the deposit, the contract balance drops to 0 Wei, the contract transitions to the
Code:
InactiveThe Contract Arguments
This section contains additional information for running the contract. We should expect that our example accounts may change with each refresh/reload of Remix.
Our contract creation argument is the deposit (twice the purchase value). We’ll assume the purchase value to be 5 Wei, making the contract creation argument very simple:
Code:
10Contract Test Scenario
- Account deploys the contract with a deposit of 10 Wei, effectively becoming a seller.Code:
0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
- Account confirms the purchase by calling theCode:
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2function and enters the trade with a deposit of 10 Wei, effectively becoming a buyer.Code:confirmPurchase()
- The buyer confirms receiving the order by calling the function.Code:
confirmReceived()
- The seller concludes the trade by calling the function.Code:
refundSeller()
Conclusion
We continued our smart contract example series with this article that implements a safe remote purchase.
First, we laid out clean source code (without any comments) for readability purposes.
Second, we dissected the code, analyzed it, and explained each possibly non-trivial segment.

