{"id":128136,"date":"2022-09-15T18:06:01","date_gmt":"2022-09-15T18:06:01","guid":{"rendered":"https:\/\/blog.finxter.com\/?p=680392"},"modified":"2022-09-15T18:06:01","modified_gmt":"2022-09-15T18:06:01","slug":"solidity-example-safe-remote-purchase","status":"publish","type":"post","link":"https:\/\/sickgaming.net\/blog\/2022\/09\/15\/solidity-example-safe-remote-purchase\/","title":{"rendered":"Solidity Example \u2013 Safe Remote Purchase"},"content":{"rendered":"\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-top\" data-payload=\"{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;680392&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;top&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;1&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;5&quot;,&quot;greet&quot;:&quot;Rate this post&quot;,&quot;legend&quot;:&quot;5\\\/5 - (1 vote)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;width&quot;:&quot;142.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}\">\n<div class=\"kksr-stars\">\n<div class=\"kksr-stars-inactive\">\n<div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div class=\"kksr-stars-active\" style=\"width: 142.5px;\">\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<div class=\"kksr-star\" style=\"padding-right: 5px\">\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/div>\n<div class=\"kksr-legend\" style=\"font-size: 19.2px;\"> 5\/5 &#8211; (1 vote) <\/div>\n<\/div>\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube\"><a href=\"https:\/\/blog.finxter.com\/solidity-example-safe-remote-purchase\/\"><img decoding=\"async\" src=\"https:\/\/blog.finxter.com\/wp-content\/plugins\/wp-youtube-lyte\/lyteCache.php?origThumbUrl=https%3A%2F%2Fi.ytimg.com%2Fvi%2FSZ-agoVo67A%2Fhqdefault.jpg\" alt=\"YouTube Video\"><\/a><figcaption><\/figcaption><\/figure>\n<p>This article continues on the <a href=\"https:\/\/blog.finxter.com\/top-solidity-smart-contract-examples-for-learning\/\" data-type=\"URL\" data-id=\"https:\/\/blog.finxter.com\/top-solidity-smart-contract-examples-for-learning\/\" target=\"_blank\" rel=\"noreferrer noopener\">Solidity Smart Contract Examples<\/a> series, which implements a simple, but the useful process of safe remote purchase. <\/p>\n<p>Here, we&#8217;re walking through an example of a blind auction (<a href=\"https:\/\/docs.soliditylang.org\/en\/v0.8.15\/solidity-by-example.html#safe-remote-purchase\" data-type=\"URL\" data-id=\"https:\/\/docs.soliditylang.org\/en\/v0.8.15\/solidity-by-example.html#safe-remote-purchase\" target=\"_blank\" rel=\"noreferrer noopener\">docs<a href=\"https:\/\/docs.soliditylang.org\/en\/v0.8.15\/solidity-by-example.html#safe-remote-purchase\"><\/a><\/a>). <\/p>\n<ul>\n<li>We&#8217;ll first lay out the entire <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.finxter.com\/introduction-to-smart-contracts-and-solidity-part-3-blockchain-basics\/\" data-type=\"post\" data-id=\"537705\" target=\"_blank\">smart contract<\/a> example without the comments for readability and development purposes. <\/li>\n<li>Then we&#8217;ll dissect it part by part, analyze it and explain it. <\/li>\n<li>Following this path, we&#8217;ll get a hands-on experience with smart contracts, as well as good practices in coding, understanding, and debugging smart contracts.<\/li>\n<\/ul>\n<h2>Smart Contract &#8211; Safe Remote Purchase<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.4;\ncontract 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); }\n}\n<\/pre>\n<h2><a><\/a>Code breakdown and analysis<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.4;\ncontract Purchase {\n<\/pre>\n<p>The state variables for recording the value, seller, and buyer addresses.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> uint public value; address payable public seller; address payable public buyer;\n<\/pre>\n<p>For the <a href=\"https:\/\/blog.finxter.com\/solidity-crash-course\/\" data-type=\"post\" data-id=\"445146\" target=\"_blank\" rel=\"noreferrer noopener\">first time<\/a>, we&#8217;re introducing the <code>enum<\/code> data structure that symbolically defines the four possible states of our contract. The states are internally indexed from <code>0<\/code> to <code>enum_length - 1<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> enum State { Created, Locked, Release, Inactive }<\/pre>\n<p>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.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> State public state;<\/pre>\n<p>The <code>condition<\/code> modifier guards a function against executing without previously satisfying the condition, i.e. an expression given alongside the function definition.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> modifier condition(bool condition_) { require(condition_); _; }\n<\/pre>\n<p>The error definitions are used with the appropriate, equally-named modifiers.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> error OnlyBuyer(); error OnlySeller(); error InvalidState(); error ValueNotEven();\n<\/pre>\n<p>The <code>onlyBuyer<\/code> modifier guards a function against executing when the function caller is not the buyer.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> modifier onlyBuyer() { if (msg.sender != buyer) revert OnlyBuyer(); _; }\n<\/pre>\n<p>The <code>onlySeller<\/code> modifier guards a function against executing when the function caller differs from the seller.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> modifier onlySeller() { if (msg.sender != seller) revert OnlySeller(); _; }\n<\/pre>\n<p>The <code>inState<\/code> modifier guards a function against executing when the contract state differs from the required <code>state_<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> modifier inState(State state_) { if (state != state_) revert InvalidState(); _; }\n<\/pre>\n<p>The events that the contract emits to acknowledge the functions <code>abort()<\/code>, <code>confirmPurchase()<\/code>, <code>confirmReceived()<\/code>, and <code>refundSeller()<\/code> were executed.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> event Aborted(); event PurchaseConfirmed(); event ItemReceived(); event SellerRefunded();\n<\/pre>\n<p>The constructor is declared as <code><a href=\"https:\/\/blog.finxter.com\/what-is-payable-in-solidity\/\" data-type=\"post\" data-id=\"37282\" target=\"_blank\" rel=\"noreferrer noopener\">payable<\/a><\/code>, meaning that the contract deployment (synonyms <em>creation<\/em>, <em>instantiation<\/em>) requires sending a value (<code>msg.value<\/code>) with the contract-creating transaction.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> constructor() payable {<\/pre>\n<p>The <code>seller<\/code> state variable is set to <code>msg.sender<\/code> address, cast (converted) to payable.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> seller = payable(msg.sender);<\/pre>\n<p>The value state variable is set to half the <code>msg.value<\/code>, 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.<\/p>\n<p class=\"has-global-color-8-background-color has-background\"><img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/72x72\/1f4a1.png\" alt=\"\ud83d\udca1\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> <strong>Info<\/strong>: <em>&#8220;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).&#8221;<\/em> (<a rel=\"noreferrer noopener\" href=\"https:\/\/www.rocketmortgage.com\/learn\/what-is-escrow\" data-type=\"URL\" data-id=\"https:\/\/www.rocketmortgage.com\/learn\/what-is-escrow\" target=\"_blank\">source<\/a>) <\/p>\n<p>In our case, our escrow is our smart contract.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> value = msg.value \/ 2;<\/pre>\n<p>If the value is not equally divided, i.e. the <code>msg.value<\/code> is not an even number, the function will terminate. Since the seller will always<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> if ((2 * value) != msg.value) revert ValueNotEven(); }\n<\/pre>\n<p>Aborting the remote safe purchase is allowed only in the <code>Created<\/code> state and only by the seller. <\/p>\n<p>The <code>external<\/code> keyword makes the function callable only by other accounts \/ smart contracts. From the business perspective, only the seller can call the <code>abort()<\/code> function and only before the buyer decides to purchase, i.e. before the contract enters the <code>Locked<\/code> state.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> function abort() external onlySeller inState(State.Created) {\n<\/pre>\n<p>Emits the <code>Aborted<\/code> event, the contract state transitions to inactive, and the balance is transferred to the seller.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> emit Aborted(); state = State.Inactive;\n<\/pre>\n<p class=\"has-global-color-8-background-color has-background\"><img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/72x72\/1f4a1.png\" alt=\"\ud83d\udca1\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> <strong>Note<\/strong>: <em>&#8220;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.&#8221;<\/em> (<a href=\"https:\/\/docs.soliditylang.org\/en\/v0.8.15\/units-and-global-variables.html\">docs<\/a>). <\/p>\n<p>In other words, this keyword lets us access the contract&#8217;s inherited members. <\/p>\n<p>Every contract inherits its members from the address type and can access these members via <code>address(this).&lt;a member><\/code> (<a href=\"https:\/\/docs.soliditylang.org\/en\/v0.8.15\/units-and-global-variables.html#address-related\" data-type=\"URL\" data-id=\"https:\/\/docs.soliditylang.org\/en\/v0.8.15\/units-and-global-variables.html#address-related\" target=\"_blank\" rel=\"noreferrer noopener\">docs<\/a>).<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> seller.transfer(address(this).balance); }\n<\/pre>\n<p>The <code>confirmPurchase()<\/code> function is available for execution only in the <code>Created<\/code> state. <\/p>\n<p>It enforces the rule that a <code>msg.value<\/code> must be twice the value of the purchase. <\/p>\n<p>The <code>confirmPurchase()<\/code> function is also declared as <code>payable<\/code>, meaning the caller, i.e. the buyer has to send the currency (<code>msg.value<\/code>) with the function call.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> function confirmPurchase() external inState(State.Created) condition(msg.value == (2 * value)) payable {\n<\/pre>\n<p>The <a href=\"https:\/\/blog.finxter.com\/ethereum-virtual-machine-evm-message-calls-solidity-smart-contracts\/\" data-type=\"post\" data-id=\"592250\" target=\"_blank\" rel=\"noreferrer noopener\">event<\/a> <code>PurchaseConfirmed()<\/code> is emitted to mark the purchase confirmation.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> emit PurchaseConfirmed();<\/pre>\n<p>The <code>msg.sender<\/code> value is cast to payable and assigned to the buyer variable.<\/p>\n<p class=\"has-global-color-8-background-color has-background\"><img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/72x72\/1f4a1.png\" alt=\"\ud83d\udca1\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> <strong>Info<\/strong>: Addresses are <em>non-payable<\/em> by design to prevent accidental payments; that&#8217;s why we have to cast an address to a payable before being able to transfer a payment.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> buyer = payable(msg.sender);<\/pre>\n<p>The state is set to <code>Locked<\/code> as seller and buyer entered the contract, i.e., our digital version of an escrow agreement.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> state = State.Locked; }\n<\/pre>\n<p>The <code>confirmReceived()<\/code> function is available for execution only in the <code>Locked<\/code> state, and only to the buyer. <\/p>\n<p>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&#8217;s deposit.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> function confirmReceived() external onlyBuyer inState(State.Locked) {\n<\/pre>\n<p>Emits the <code>ItemReceived()<\/code> event.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> emit ItemReceived();<\/pre>\n<p>Changes the state to Release.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> state = State.Release;<\/pre>\n<p>Transfers the deposit to the buyer.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> buyer.transfer(value); }\n<\/pre>\n<p>The <code>refundSeller()<\/code> function is available for execution only in the <code>Release<\/code> state, and only to the seller. <\/p>\n<p>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.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> function refundSeller() external onlySeller inState(State.Release) {\n<\/pre>\n<p>Emits the <code>SellerRefunded()<\/code> event.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> emit SellerRefunded();<\/pre>\n<p>Changes the state to <code>Inactive<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> state = State.Inactive;<\/pre>\n<p>Transfers the deposit of two value amounts and the one earned value amount to the seller.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> seller.transfer(3 * value); }\n}\n<\/pre>\n<p>Our <a href=\"https:\/\/blog.finxter.com\/top-solidity-smart-contract-examples-for-learning\/\" data-type=\"post\" data-id=\"663675\" target=\"_blank\" rel=\"noreferrer noopener\">smart contract example<\/a> of a safe remote purchase is a nice and simple example that demonstrates how a purchase may be conducted on the <a href=\"https:\/\/blog.finxter.com\/smart-contracts-and-evm\/\" data-type=\"post\" data-id=\"92507\" target=\"_blank\" rel=\"noreferrer noopener\">Ethereum blockchain<\/a> network. <\/p>\n<p>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. <\/p>\n<p>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>Locked<\/code> state.<\/p>\n<p>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. <\/p>\n<p>When the buyer confirms that he received the goods he purchased, the contract will transition to the <code>Release<\/code> state, and the purchase value will be released to the buyer.<\/p>\n<p>The seller can now withdraw his earned purchase value with the deposit, the contract balance drops to 0 Wei, the contract transitions to the <code>Inactive<\/code> state, and the safe remote purchase concludes with execution.<\/p>\n<h2>The Contract Arguments<\/h2>\n<p>This section contains additional information for running the contract. We should expect that our example accounts may change with each refresh\/reload of Remix.<\/p>\n<p>Our contract creation argument is the <em>deposit <\/em>(twice the purchase value). We&#8217;ll assume the purchase value to be 5 Wei, making the contract creation argument very simple:<\/p>\n<p><code>10<\/code><\/p>\n<h2><a><\/a>Contract Test Scenario<\/h2>\n<ol type=\"1\">\n<li>Account <code>0x5B38Da6a701c568545dCfcB03FcB875f56beddC4<\/code> deploys the contract with a deposit of 10 Wei, effectively becoming a seller.<\/li>\n<li>Account <code>0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2<\/code> confirms the purchase by calling the <code>confirmPurchase()<\/code> function and enters the trade with a deposit of 10 Wei, effectively becoming a buyer.<\/li>\n<li>The buyer confirms receiving the order by calling the <code>confirmReceived()<\/code> function.<\/li>\n<li>The seller concludes the trade by calling the <code>refundSeller()<\/code> function.<\/li>\n<\/ol>\n<h2><a><\/a>Conclusion<\/h2>\n<p>We continued our smart contract example series with this article that implements a safe remote purchase.<\/p>\n<p>First, we laid out clean source code (without any comments) for readability purposes.<\/p>\n<p>Second, we dissected the code, analyzed it, and explained each possibly non-trivial segment.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/academy.finxter.com\/university\/solidity-building-blocks-an-advanced-introduction-towards-smart-contract-development-on-ethereum\/\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-291.png\" alt=\"\" class=\"wp-image-387294\" width=\"363\" height=\"650\" srcset=\"https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-291.png 363w, https:\/\/blog.finxter.com\/wp-content\/uploads\/2022\/05\/image-291-168x300.png 168w\" sizes=\"auto, (max-width: 363px) 100vw, 363px\" \/><\/a><\/figure>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>5\/5 &#8211; (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&#8217;re walking through an example of a blind auction (docs). We&#8217;ll first lay out the entire smart contract example without the comments for readability and development purposes. Then [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[857],"tags":[73,468,528],"class_list":["post-128136","post","type-post","status-publish","format-standard","hentry","category-python-tut","tag-programming","tag-python","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/128136","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/comments?post=128136"}],"version-history":[{"count":0,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/128136\/revisions"}],"wp:attachment":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media?parent=128136"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/categories?post=128136"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/tags?post=128136"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}