پیشرفته کریپتو پدیا

آموزش تخصصی ایجاد و نصب یک برنامه غیرمتمرکز (Dapp) روی اتریوم!

در این آموزش قصد داریم که شما را با مراحل مورد نیاز برای ایجاد یک برنامه غیر متمرکز (Dapp) از شروع تا پایان روی اتریوم آشنا کنیم. این کار به این نحو صورت می گیرد:

اتریوم

هدف از نوشتن این مقاله آموزشی این است که مقالات زیادی را در این رابطه می بینیم که ناقص هستند و همه جوانب قضیه و داشتن یک برنامه غیر متمرکز نهایی که شامل میزبانی غیر متمرکز با IPFS که یک فناوری قدرتمند است را نشان نمی دهند.

از طرف دیگر وقتی راجع به اتریوم صحبت می شود و گفته می شود این شبکه قابلیت نوشتن برنامه های غیرمتمرکز (Dapp) روی خودش را دارد؛ بسیاری کمی گیج می شوند و متوجه نمی شوند واقعا این ویژگی یعنی چه و میتواند چه کاربردی داشته باشد.

در این مقاله تعدادی از موضوعات پیچیده و اساسی را یاد می گیرید که از جمله آنها می توان به موارد زیر اشاره کرد:

  • نحوه ایجاد قرارداد هوشمند از ابتدای کار
  • نحوه نصب و توسعه قرارداد های هوشمند بر ropsten
  • نحوه ایجاد فرانت اند یک برنامه غیر متمرکز (Dapp)
  • نحوه اتصال قرارداد نصب شده با برنامه شما
  • نحوه توسعه برنامه غیر متمرکز نهایی در میزبان غیر متمرکز IPFS
  • نحوه استفاده از دامنه سفارشی خود با IPFS

در انتهای این مقاله آموزشی، شما یک وبسایت کاملا غیر متمرکز خواهید داشت که با قرارداد های هوشمند خودتان کار می کند. بنابراین می توانید آنها را از مرورگر به کار ببرید و با استفاده از متامسک (Metamask) یا Mist با بلاک چین تعامل داشته باشید.

برای شروع لازم شما در مورد بلاک چین و ایجاد نرم افزار بدانید و دانش اساسی در مورد نحوه کار قرارداد ها داشته باشید و تجربه برنامه نویسی سایت ها با جاوا اسکریپت را داشته باشید.

اگر شما توسعه دهنده وب هستید، این مقاله می تواند برای شما ایده آل باشد. اگر می خواهید شروع به ساختن چیزی در اتریوم کنید و به یک توسعه دهنده برنامه های غیر متمرکز تبدیل شوید، این مقاله مفید می باشد.

کتابی در رابطه با توسعه اتریوم و برای یادگیری سالیدیتی Solidity توسط نویسنده این مقاله نوشته شده که در آنجا مهمترین مفاهیم برای اینکه سریعا یک توسعه دهنده اتریوم شوید را یاد می گیرید. برای خریداری این کتاب هم میتوانید به اینجا مراجعه کنید.

این فناوری که ما قصد داریم مورد استفاده قرار دهیم شامل موارد زیر است:

پایگاه داده: بلاک چین Testnet Ropsten اتریوم (یکی از شبکه های آزمایشی اتریوم که کدنویسان روی آن کار میکنند)

میزبانی: IPFS برای دریافت میزبانی رایگان برای همیشه در یک پلتفرم غیر متمرکز

فرانت اند: React.js با webpack. نگران نباشید زیرا مهمترین مرحله ها توضیح داده خواهد شد. می توانید هر چارچوبی را که می خواهید مورد استفاده قرار دهید و یا به طور ساده می توانید از جاوا اسکریپت استفاده کنید.

نام دامنه: Godaddy. در اینجا می توان سرویس دامنه غیر متمرکز مانند Peername را مورد استفاده قرار داد اما با Godaddy یا هر ثبت کننده دامنه دیگری کار سریعتر و آسان تر انجام می گیرد.

زبان برنامه نویسی قرارداد: Solidity ۰.۴.۱۱ در حال حاضر مشهورترین زبان برنامه نویسی برای توسعه قرارداد های هوشمند است.

قرارداد های فرانت اند: Web3.js برای قرارداد هایی به کار می رود که در سطح کاربری شبکه شما قرار دارند.

چارچوب ها: Truffle برای نصب، آزمایش و ترکیب قرارداد های ما.

سرور توسعه: Node.js برای استفاده از این برنامه به کار می رود در حالی که به طور موضعی همراه با testrpc توسعه پیدا می کند.

متاماسک (Metamask): برای استفاده از برنامه نهایی همانند آنچه که کاربر نهایی انجام خواهد داد.

و اینها مراحلی هستند که ما باید از آنها گذر کنیم:

۱- سازمان دادن به پروژه

۲- برنامه نویسی کردن قرارداد های سالیدیتی (Solidity)

۳- ایجاد برنامه فرانت اند

۴- نصب و گسترش آنلاین برنامه با IPFS

۵- استفاده از یک دامنه سفارشی برای برنامه

۶- کار با برنامه غیر متمرکز نهایی

۱- سازمان دادن پروژه

من توضیح می دهم که ما قصد ایجاد چه چیزی را داریم، بنابراین شما تصویر واضحی از نتیجه نهایی خواهید داشت. ما قصد داریم یک برنامه شبیه بازی شرطبندی ایجاد کنیم که در آن کاربران قادر به شرط بندی پول خود بین اعداد ۱ و ۱۰ باشند و در صورتی که شرط بندی آنها درست از آب درآید، آنها بخشی از همه پول را برنده می شوند که تا بعد از ۱۰۰ بار شرط بندی گرو گذاشته شده است.

این برنامه برای شروع کاملا پیچیده است اما شما آن را دوست خواهید داشت زیرا برنامه ای اورجینال است و استفاده از آن لذت بخش است. حال، من باید به شما این هشدار را بدهم که این برنامه ایمن نیست زیرا با استفاده از Solidity نمی توانید حالت تصادفی بودن را ایجاد کنید. شما باید از فنون مختلف ایجاد وضعیت تصادفی در بلاک چین استفاده کنید و مشاهده کنید که چه چیزی کار می کند.

این مقاله آموزشی را به عنوان یک مقدمه در نظر بگیرید تا کل فرآیند ایجاد برنامه غیر متمرکز از ابتدا تا پایان را مورد مشاهده قرار دهید. ابتدا، نسخه حال حاضر node.js را از وبسایت nodejs.org دانلود کنید، البته اگر از قبل nodejs ندارید. سپس یک پوشه بنام casino-ethereum در دسکتاپ کامپیوتر خود ایجاد کنید. در داخل، terminal یا command line را باز کنید و فرمان npm init-y را به اجرا درآورید. بعد از آن فرمان زیر را انجام دهید:

npm i -D -g truffle

برای نصب چارچوب توسعه اتریوم Truffle در این پروژه به عنوان یک وابستگی در حال توسعه (-D) و به طور جهانی (-g) اقدام کنید. سپس فرمان زیر را اجرا کنید:

npm init -y
truffle init

این دو فرمان پروژه را با ایجاد فایل های اساسی برای توسعه برنامه غیر متمرکز، شروع خواهند کرد. بعد از آن فرمان زیر را انجام دهید:

npm i -D webpack react react-dom babel-core babel-loader babel-preset-react babel-preset-env css-loader style-loader json-loader [email protected]

آنها همه ابزار هایی هستند که ما برای ایجاد فرانت اند برنامه غیر متمرکز نیاز داریم. وابستگی های زیادی وجود دارد زیرا ما یک برنامه وب را با آخرین نسخه جاوا اسکریپت و react.js ایجاد خواهیم کرد. در اصل ما webpack، react، babel و web3 را نصب کرده ایم. توجه داشته باشید که ما در حال نصب نسخه ۰.۲۰.۰ از web3 با [email protected] هستیم زیرا آخرین نسخه Web3 یعنی ۱.۰ هنوز در حال تست و ناپایدار می باشد.

سپس npm I –g http-server را نصب کنید. این یک سرور سبک است که شما برای میزبانی برنامه شبکه به طور محلی همزمان که در حال توسعه بر localhost: ۸۰۸۰ هستید، مورد استفاده قرار می دهید.

حالا همه متعلقات مورد نیاز را برای ایجاد این پروژه با react و webpack دارید.

بعد از آن به فولدر پروژه خود بروید و و یک فایل webpack.config.js را ایجاد نمایید. این فایلی است که همه جاوا اسکریپت و css ما را برای ایجاد یک فایل خاص بنام build.js ترکیب می کند که همه کد js تغییر می یابد و با مرورگر های جدید و قدیمی سازگار می شود. بنابراین می توان از ویژگی های آخرین نسخه جاوا اسکریپت که هنوز بیرون داده نشده اند، استفاده کرد. حال این کد را داخل فایل بنویسید:

const path = require('path')
module.exports = {
   entry: path.join(__dirname, 'src/js', 'index.js'), // Our frontend will be inside the src folder
   output: {
      path: path.join(__dirname, 'dist'),
      filename: 'build.js' // The final file will be created in dist/build.js
   },
   module: {
      rules: [{
         test: /\.css$/, // To load the css in react
         use: ['style-loader', 'css-loader'],
         include: /src/
      }, {
         test: /\.jsx?$/, // To load the js and jsx files
         loader: 'babel-loader',
         exclude: /node_modules/,
         query: {
            presets: ['es2015', 'react', 'stage-2']
         }
      }]
   }
}

بعد از آن، فولدر src/ را در پروژه ایجاد کنید و در داخل آن فولدر، فولدر های js/ و css/ تنها برای سازمان دهی کد مبدا منبع ایجاد کنید.

در داخل هر فولدر index.js و index.css را ایجاد کنید.

سرانجام فولدر dist/ در سطح خارجی و داخل index.html ایجاد کنید.

این ساختار مانند زیر خواهد بود:

contracts/
-- Migrations.sol
migrations/
node_modules/
test/
src/
-- css/index.css
-- js/index.js
dist/
-- index.html
package.json 
truffle-config.js
truffle.js
webpack.config.js

حالا به index.html خود بروید و این کد را به صورت دستی بنویسید:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
   <title>Casino Ethereum Dapp</title>
</head>
<body>
   <div id="root"></div>
   <script src="build.js"></script>
</body>
</html>

چیز های اصلی در مورد این کد این است که من فونت Open sans را اضافه کرده ام تا متن بهتر به نظر برسد. <div id=”root”></div> در جایی که کد react درج می شود و <script scr=”build.js”></script> برای داخل شدن در فایل build که توسط webpack ایجاد شده، مورد استفاده قرار می گیرد.

فایل contracts/Casino.sol را ایجاد کنید، این قرارداد Solidity اصلی است که ما در بخش بعد کدگذاری خواهیم کرد. بعد از آماده کردن همه اینها، سرانجام می توانیم ایجاد برنامه را شروع کنیم.

۲- برنامه نویسی کردن قرارداد های Solidity

شما در این بخش، قرارداد اصلی پروژه را خواهید نوشت و آن را تست می کنید تا ببینید که آیا بدون نصب کد آسیب پذیر که می تواند سبب از دست دادن پول واقعی افراد شود، به درستی کار می کند.

در ابتدا، هر قرارداد Solidity باید با نسخه کامپایلر شروع شود، بنابراین این را در بالای فایل casino.sol ایجاد شده، بنویسید:

pragma solidity 0.4.20;

حالا قرارداد را ایجاد کنید:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   function Casino() public {
      owner = msg.sender;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
}

متغیر آدرس مالک (owner) نامیده می شود که رشته درازی از اکانت Metamask شماست و در مورد من رشته زیر می باشد:

0x08f96d0f5C9086d7f6b59F9310532BdDCcF536e2

Function casino () سازنده است زیرا همان نام قرارداد را دارد و ما آن را برای سازمان دادن مالک آن قرارداد مورد استفاده قرار می دهیم. آدرس کاربری که این قرارداد را ایجاد کرده است، مالک می باشد.

Function kill () برای انهدام قرارداد در هر زمانی که بخواهید مورد استفاده قرار می گیرد. البته تنها مالک می تواند آن را منهدم کند. اتر باقیمانده که قرارداد ذخیره کرده است برای آدرس مالک فرستاده خواهد شد. تنها وقتی از آن استفاده کنید که قرارداد توسط هک به خطر افتاده باشد و شما قادر به حفظ امنیت آن نباشید.

به یاد داشته باشید که هدف ایجاد یک برنامه شبیه بازی شرطبندی است که کاربران قادر به شرط بندی پول خود برای عددی بین ۱ و ۱۰ است و اگر حدس آنها صحیح باشد، آنها مقداری از همه پول که تا صد مورد شرط بندی گرو گذاشته شده است، برنده خواهند شد.

پس ما باید وظایف زیر را انجام دهیم:

ثبت کردن اینکه هر کاربر چه مقدار بت (bet) برای چه عددی دارد.

کمترین قیمت یک بت

مقدار کل اتر انباشته شده

متغیری برای ذخیره تعداد بت های موجود

زمانی که بت ها متوقف می شوند و شماره برنده ظاهر می شود.

عملکرد ارسال مقدار اتر برنده شده برای هر برنده

اینها هم عملکرد ها می باشند:

شرط بستن برای یک عدد

ایجاد شماره تصادفی به عنوان برنده

فرستادن اتر به برندگان

ثبت کردن اینکه هر کاربر چه مقدار برای هر شماره شرط بندی کرده است

ما می توانیم از struct با نوع متغیر mapping برای ذخیره آدرس کاربر، مقدار بت و عدد در یک جاوا اسکریپت مانند object استفاده کنیم.

یک struct مانند یک object در جاوا اسکریپت است و mapping نیز مانند یک آرایه است.

اجازه دهید به سراغ کد برویم:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   uint256 public minimumBet;
   uint256 public totalBet;
   uint256 public numberOfBets;
   uint256 public maxAmountOfBets = 100;
   address[] public players;
   struct Player {
      uint256 amountBet;
      uint256 numberSelected;
   }
   // The address of the player and => the user info   
   mapping(address => Player) public playerInfo;
   function Casino() public {
      owner = msg.sender;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
}

این struct، player نامیده می شود و از مقدار بت و عدد انتخاب شده ساخته شده است زیرا می خواهیم مقدار اتری که هر کاربر شرط بندی کرده و عدد شرط بندی شده را ثبت کنیم.

سپس یک mapping بنام PlayerInfo ایجاد می شود تا چیزی مانند playerInfo [here_goes_his_address].amountBet را انجام دهد که در آنجا می توانیم آدرس را به عنوان کلید در Object جاوا اسکریپت برای دسترسی به ویژگی های آن مورد استفاده قرار دهیم.

ما می خواهیم یک صف از بازی کنندگان داشته باشیم تا بدانیم چه کسی در حال بازی کردن است و بتوانیم پاداش ها را در میان برندگان توزیع کنیم.

سازنده (constructor) را برای تعریف کوچکترین بت بازی تعدیل کنید، بنابراین می توانید یک عدد متغیر از بت های مینیمم برای توزیع جایزه ها انتخاب کنید:

function Casino(uint256 _minimumBet){
   owner = msg.sender;
   if(_minimumBet != 0 ) minimumBet = _minimumBet;
}

حال عملکردی در انتهای قرارداد برای شرط بندی اعداد بین ۱ و ۱۰ ایجاد کنید که شامل دستور زیر است:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   uint256 public minimumBet;
   uint256 public totalBet;
   uint256 public numberOfBets;
   uint256 public maxAmountOfBets = 100;
   address[] public players;
   struct Player {
      uint256 amountBet;
      uint256 numberSelected;
   }
   // The address of the player and => the user info   
   mapping(address => Player) public playerInfo;
   function Casino(uint256 _minimumBet) public {
      owner = msg.sender;
      if(_minimumBet != 0 ) minimumBet = _minimumBet;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
   // To bet for a number between 1 and 10 both inclusive
   function bet(uint256 numberSelected) public payable {
      require(!checkPlayerExists(msg.sender));
      require(numberSelected >= 1 && numberSelected <= 10);
      require(msg.value >= minimumBet);
      playerInfo[msg.sender].amountBet = msg.value;
      playerInfo[msg.sender].numberSelected = numberSelected;
      numberOfBets++;
      players.push(msg.sender);
      totalBet += msg.value;
   }
}

کلمه payable یک modifier می باشد که برای نشان دادن این به کار می رود که این عملکرد وقتی که شما ان را اجرا می کنید، می تواند اتر دریافت کند.

عملکرد require () مانند یک اظهار شرطی است که باید به طور صحیح برگردد. اگر شرایط داخل require به طور غلط ارزیابی شود، عملکرد متوقف می شود و اتر پرداخته شده به فرستنده بازگردادنده می شود. ما از آن برای تایید این استفاده می کنیم که بازیگر از قبل بازی نکرده است و اینکه عدد در آن دامنه است و اینکه بت پرداختی بیشتر و یا مساوی با حداقل بت می باشد.

Msg.sender و msg.value ارزش هایی هستند که توسط کاربر رمانی که قرارداد را اجرا می کند، تعریف می شوند. فرستنده آدرس کاربر می باشد و ارزش مقدار اتری است که او زمانی که در حال اجرای عملکرد payable است، پرداخته است.

سپس مقدار بت را برای آن کاربر با playerInfo [msg.sender] .amountBet=msg.sender;.msg.sender تعریف می کنیم که آدرس کاربری است که آن عملکرد را اجرا کرده است.

سپس ما numberofbets را به اندازه ۱ افزایش می دهیم. این متغیر استفاده متقابلی دارد تا تعداد بت های موجود برای این بازی مشاهده شود و وقتی این بت ها به ۱۰۰ رسیدند و پاداش ها توزیع شدند، توقف انجام گیرد.

سرانجام ما بت totalAmount را برای این بازی افزایش می دهیم که در میان برندگان توزیع خواهد شد.

در اولین اظهار require آن عملکرد چنین می بینید:

require(!checkPlayerExists(msg.sender));

من به عملکردی بنام checkPlayerExists () مراجعه می کنم تا به بررسی این بپردازم که کاربر مورد نظر قبلا بازی کرده است یا نه زیرا ما می خواهیم که هر شخص تنها یک بار در هر بازی، بازی کند. عملکرد هنوز موجود نیست پس اجازه دهید حالا آن را ایجاد کنیم:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   uint256 public minimumBet;
   uint256 public totalBet;
   uint256 public numberOfBets;
   uint256 public maxAmountOfBets = 100;
   address[] public players;
   struct Player {
      uint256 amountBet;
      uint256 numberSelected;
   }
   // The address of the player and => the user info   
   mapping(address => Player) public playerInfo;
   function Casino(uint256 _minimumBet) public {
      owner = msg.sender;
      if(_minimumBet != 0 ) minimumBet = _minimumBet;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
   function checkPlayerExists(address player) public constant returns(bool){
      for(uint256 i = 0; i < players.length; i++){
         if(players[i] == player) return true;
      }
      return false;
   }
   // To bet for a number between 1 and 10 both inclusive
   function bet(uint256 numberSelected) public payable {
      require(!checkPlayerExists(msg.sender));
      require(numberSelected >= 1 && numberSelected <= 10);
      require(msg.value >= minimumBet);
      playerInfo[msg.sender].amountBet = msg.value;
      playerInfo[msg.sender].numberSelected = numberSelected;
      numberOfBets++;
      players.push(msg.sender);
      totalBet += msg.value;
   }
}

کلمه کلیدی constant این عملکرد نشان می دهد که این عملکردی است که هزینه ای برای اجرا ندارد و دلیل این است که یک ارزش از قبل موجود را از بلاک چین باز می گرداند. آن در حال خواندن یک ارزش است و به همین دلیل رایگان می باشد.

بعد از ایجاد این عملکرد های عالی، ما باید بررسی کنیم که آیا تعداد بت ها بزرگتر یا مساوی با تعداد بیشینه است، بنابراین می توانیم عدد برنده را ایجاد کنیم زیرا وقتی ۹۹ بت موجود است، ما می خواهیم که بت بعدی یعنی بت شماره ۱۰۰ قیمت ها را بیرون داده و محاسبات مورد نیاز را اجرا کند.

حال این را در انتهای عملکرد bet() به همین خاطر اضافه نمایید:

if(numberOfBets >= maxAmountOfBets) generateNumberWinner();

حال ما باید عملکرد generateNumberWinner را ایجاد کنیم که به طور تصادفی یک عدد بین ۱ و ۱۰ ایجاد می کند تا در مورد برنده تصمیم گیری کند:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   uint256 public minimumBet;
   uint256 public totalBet;
   uint256 public numberOfBets;
   uint256 public maxAmountOfBets = 100;
   address[] public players;
   struct Player {
      uint256 amountBet;
      uint256 numberSelected;
   }
   // The address of the player and => the user info   
   mapping(address => Player) public playerInfo;
   function Casino(uint256 _minimumBet) public {
      owner = msg.sender;
      if(_minimumBet != 0 ) minimumBet = _minimumBet;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
   function checkPlayerExists(address player) public constant returns(bool){
      for(uint256 i = 0; i < players.length; i++){
         if(players[i] == player) return true;
      }
      return false;
   }
   // To bet for a number between 1 and 10 both inclusive
   function bet(uint256 numberSelected) public payable {
      require(!checkPlayerExists(msg.sender));
      require(numberSelected >= 1 && numberSelected <= 10);
      require(msg.value >= minimumBet);
      playerInfo[msg.sender].amountBet = msg.value;
      playerInfo[msg.sender].numberSelected = numberSelected;
      numberOfBets++;
      players.push(msg.sender);
      totalBet += msg.value;
   }
   // Generates a number between 1 and 10 that will be the winner
   function generateNumberWinner() public {
      uint256 numberGenerated = block.number % 10 + 1; // This isn't secure
      distributePrizes(numberGenerated);
   }
}

آن شماره بلاک حال حاضر را می گیرد و به آخرین شماره ۱+ می رسد، بنابراین اگر شماره بلاک ۱۲۸۱۴۲ باشد، عدد ایجاد شده ۱۲۸۱۴۲%۱۰=۲ و ۲+۱=۳ می باشد.

این ایمن نیست زیرا دانستن این که بر طبق شرایط چه عددی برنده خواهد بود، آسان است. ماینر ها می توانند به خاطر منفعت خود تصمیم به رویت عدد بلاک بگیرند.

سپس جایزه ها را برای برندگان با عملکرد distributeprices (numberGenerated) توزیع می کنیم:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   uint256 public minimumBet;
   uint256 public totalBet;
   uint256 public numberOfBets;
   uint256 public maxAmountOfBets = 100;
   address[] public players;
   struct Player {
      uint256 amountBet;
      uint256 numberSelected;
   }
   // The address of the player and => the user info   
   mapping(address => Player) public playerInfo;
   function Casino(uint256 _minimumBet) public {
      owner = msg.sender;
      if(_minimumBet != 0 ) minimumBet = _minimumBet;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
   function checkPlayerExists(address player) public constant returns(bool){
      for(uint256 i = 0; i < players.length; i++){
         if(players[i] == player) return true;
      }
      return false;
   }
   // To bet for a number between 1 and 10 both inclusive
   function bet(uint256 numberSelected) public payable {
      require(!checkPlayerExists(msg.sender));
      require(numberSelected >= 1 && numberSelected <= 10);
      require(msg.value >= minimumBet);
      playerInfo[msg.sender].amountBet = msg.value;
      playerInfo[msg.sender].numberSelected = numberSelected;
      numberOfBets++;
      players.push(msg.sender);
      totalBet += msg.value;
   }
   // Generates a number between 1 and 10 that will be the winner
   function generateNumberWinner() public {
      uint256 numberGenerated = block.number % 10 + 1; // This isn't secure
      distributePrizes(numberGenerated);
   }
   // Sends the corresponding ether to each winner depending on the total bets
   function distributePrizes(uint256 numberWinner) public {
      address[100] memory winners; // We have to create a temporary in memory array with fixed size
      uint256 count = 0; // This is the count for the array of winners
      for(uint256 i = 0; i < players.length; i++){
         address playerAddress = players[i];
         if(playerInfo[playerAddress].numberSelected == numberWinner){
            winners[count] = playerAddress;
            count++;
         }
         delete playerInfo[playerAddress]; // Delete all the players
      }
      players.length = 0; // Delete all the players array
      uint256 winnerEtherAmount = totalBet / winners.length; // How much each winner gets
      for(uint256 j = 0; j < count; j++){
         if(winners[j] != address(0)) // Check that the address in this fixed array is not empty
         winners[j].transfer(winnerEtherAmount);
      }
   }
}

این عملکرد موارد زیر را انجام می دهد:

۱- ابتدا صفی از برندگان را بنام winners ایجاد می کند که با بررسی اینکه آیا numberSelected بازیگر numberWinner است یا نه آن را انجام می دهد. آرایه winners یک آرایه memory است که بعد از اجرای عملکرد حذف می شود. آن باید یک اندازه ثابت داشته باشد.

۲- سپس آن مقدار اتری را که هر برنده با توجه به مقدار بت کلی و تعداد برندگان دریافت می کند، محاسبه خواهد کرد. هر چه تعداد برندگان کمتر باشد، جایزه بزرگتر خواهد بود.

۳- سپس آن مقدار متناظر از اتر را برای هر برنده با winners [j] .transfer می فرستد.

حال ما یک قرارداد کاری داریم که به افراد اجازه می دهد که به عنوان یک بازی شرطبندی ساده به بازی بپردازند.

یکی از آخرین چیز های دیگری که دوست دارم انجام دهم، ایجاد یک fallback function ناشناس می باشد. یک عملکرد بدون نام که تعدیل دهنده payable دارد و وقتی که اتر را به قرارداد می فرستید، بدون اجرای هیچ عملکردی اجرا می شود:

// Fallback function in case someone sends ether to the contract so it doesn't get lost and to increase the treasury of this contract that will be distributed in each game
   function() public payable {}

این به شما اجازه می دهد که اتر فرستاده شده به قرارداد را ذخیره کنید. در غیر این صورت از آن امتناع می شود.

قرارداد کاملی که شما به تازگی ایجاد کرده اید به شکل زیر می باشد:

pragma solidity 0.4.20;
contract Casino {
   address public owner;
   uint256 public minimumBet;
   uint256 public totalBet;
   uint256 public numberOfBets;
   uint256 public maxAmountOfBets = 100;
   address[] public players;
   struct Player {
      uint256 amountBet;
      uint256 numberSelected;
   }
   // The address of the player and => the user info   
   mapping(address => Player) public playerInfo;
   function() public payable {}
   function Casino(uint256 _minimumBet) public {
      owner = msg.sender;
      if(_minimumBet != 0 ) minimumBet = _minimumBet;
   }
   function kill() public {
      if(msg.sender == owner) selfdestruct(owner);
   }
   function checkPlayerExists(address player) public constant returns(bool){
      for(uint256 i = 0; i < players.length; i++){
         if(players[i] == player) return true;
      }
      return false;
   }
   // To bet for a number between 1 and 10 both inclusive
   function bet(uint256 numberSelected) public payable {
      require(!checkPlayerExists(msg.sender));
      require(numberSelected >= 1 && numberSelected <= 10);
      require(msg.value >= minimumBet);
      playerInfo[msg.sender].amountBet = msg.value;
      playerInfo[msg.sender].numberSelected = numberSelected;
      numberOfBets++;
      players.push(msg.sender);
      totalBet += msg.value;
   }
   // Generates a number between 1 and 10 that will be the winner
   function generateNumberWinner() public {
      uint256 numberGenerated = block.number % 10 + 1; // This isn't secure
      distributePrizes(numberGenerated);
   }
   // Sends the corresponding ether to each winner depending on the total bets
   function distributePrizes(uint256 numberWinner) public {
      address[100] memory winners; // We have to create a temporary in memory array with fixed size
      uint256 count = 0; // This is the count for the array of winners
      for(uint256 i = 0; i < players.length; i++){
         address playerAddress = players[i];
         if(playerInfo[playerAddress].numberSelected == numberWinner){
            winners[count] = playerAddress;
            count++;
         }
         delete playerInfo[playerAddress]; // Delete all the players
      }
      players.length = 0; // Delete all the players array
      uint256 winnerEtherAmount = totalBet / winners.length; // How much each winner gets
      for(uint256 j = 0; j < count; j++){
         if(winners[j] != address(0)) // Check that the address in this fixed array is not empty
         winners[j].transfer(winnerEtherAmount);
      }
   }

شما می توانید قرارداد به روز و کامل را در github من به آدرس https://github.com/merlox/casino-ethereum در داخل پوشه contracts مشاهده کنید. آن یک نسخه پیشرفته است که داده ها را از جهان واقعی برای ایجاد عدد تصادفی ایمن فراهم می آورد.

حال که قرارداد انجام شد، ما دو گزینه داریم:

نوشتن تست هایی با استفاده از truffle و نصب قرارداد در testrpc در خلال آن تست ها

آزمایش قرارداد به صورت دستی و با استفاده از IDE متعلق به Solidity با آدرس https://remix.ethereum.org که این یک ابزار رسمی برای نوشتن، آزمایش و نصب قرارداد هاست.

برای جلوگیری از طولانی شدن این مقاله، ما از remix IDE برای آزمایش سریع قرارداد استفاده می کنیم، اگرچه باید تست ها را با truffle انجام دهید تا مطمئن شوید که برنامه ها بر طبق انتظار کار می کنند و هر زمان که آنها را به روز رسانی می کنید، ایمن هستند.

قبل از ادامه دادن باید metamask را از آدرس metamask.io دانلود کنید. از این ابزار برای اجرای قرارداد های هوشمند بر روی مرورگر و همچنین یک کیف پول اتریوم استفاده می شود.

بعد از نصب، آن را باز کنید و مراحل را دنبال کرده و یک اکانت بسازید. مطمئن شوید که عبارت عبور 12 کلمه ای را ذخیره کنید زیرا آن تنها راه برای بازیابی اکانت در حالتی است که رمز عبور یا کلید خصوصی خود را گم کرده اید. شما باید چیزی مانند این را ببینید:

اتریوم

شما حالا یک اکانت در metamask دارید و می توانید از آن برای تعامل با برنامه های غیر متمرکز و قرارداد های هوشمند استفاده کنید.

پس به Remix IDE بروید و قرارداد را درج کنید:

اتریوم

چیز هایی که ما قصد داریم در remix IDE انجام دهیم، موارد زیر می باشند:

نصب و گسترش قرارداد به Testnet Ropsten Blockchain که در این بلاک چین، اتر ارزش واقعی ندارد.

استفاده از metamask برای پرداخت gas برای هر عملکرد

اطمینان حاصل کنید که همه عملکرد ها بر طبق انتظار کار می کنند.

شما دو پنل دارید، در پنل سمت چپ کد شما قرار دارد و در پنل سمت راست نیز می توانید چیز های جالبی را با قرارداد انجام دهید.

در پنل سمت راست، بر روی تب Run کلیک کنید:

اتریوم

شما می بینید که دکمه قرمز بنام create موجود است که یک فیلد ورودی با یک عامل دارد که نوشته شده unit256_minimumBet. آن کمترین بت constructor می باشد. شما می توانید آن را خالی بگذارید و یا یک عدد در آن بنویسید.

حال افزونه Metamask را باز کنید و شبکه خود را مانند تصویر به Ropsten Testnet Network تغییر دهید.

سپس به https://faucet.metamask.io بروید و یک اتر رایگان درخواست کنید. این فاست مکانی است که شما در آن می توانید برای اکانت ropsten خود اتر دریافت کنید.

بعد از مدتی می بینید که در اکانت شما مقدار قابل ملاحظه ای اتر وجود دارد.

اتریوم

به remix IDE بروید و مطمئن باشید که شما در ان محیط هستید: Injected Web3:

اتریوم

این یعنی که شما از بلاک چین تزریق شده از Metamask استفاده می کنید. حال بر روی create کلیک کنید تا قرارداد خود را ایجاد نمایید، یک پنجره از جانب metamask توجه شما را جلب خواهد کرد:

اتریوم

تنها بر روی submit برای پرداخت برای ایجاد قرارداد کلیک کنید. حال قرارداد شما بر شبکه Ropsten موجود است.

اگر به دلیلی نتوانید محیط را تغییر دهید و یا پنجره هشدار را نبینید، کلید F12 را فشار دهید تا ابزار های توسعه کروم باز شود. بر روی دکمه reload در کروم کلیک کرده و آن را نگه دارید و آخرین گزینه را انتخاب نمایید:

اتریوم

آن باید خطا ها را تعمیر کند.

بعد از ایجاد قرارداد، آدرس آن، عملکرد ها و داده های جالب تر را بر روی پنل راست می بینید. چیزی مانند شکل پایین:

اتریوم

در بالا دکمه ای را می بینید که نوشته شده copy address و می توانید از آن برای ذخیره آدرس قرارداد برای استفاده بعدی استفاده کنید. بر روی آن کلیک کنید و آدرس را در جایی ذخیره کنید.

سپس دکمه هایی آبی قرار دارند که عملکرد ها و متغیر های constant یا public می باشند. این یعنی وقتی شما یک متغیر عمومی مانند uint256 public totalBet را اظهار می دارید، شما می توانید هر زمان که بخواهید به ارزش آن دسترسی داشته باشید زیرا عمومی است.

می توان گفت که آن یک روش گیرنده ای دارد که به طور خودکار ایجاد می شود.

سپس عملکرد های constant آنهایی هستند که ارزش را برمی گردانند و وضعیت قرارداد را تعدیل نمی کنند. این یعنی آنها متغیر های قرارداد را تعدیل نمی کنند، آنها فقط اجرا می کنند و به شما نتیجه می دهند.

اجازه دهید به اجرای یک به یک عملکرد ها بپردازیم.

اگر بر روی minimumBet کلیک کنید، فورا ارزش آن متغیر را دریافت می کنید زیرا عمومی است:

اتریوم

آن unit۲۵۶: ۱۰۰۰۰۰۰۰۰۰۰۰۰ را به صورت Wei برمی گرداند که ۰.۱ اتر است. به یاد داشته باشید که شما با استفاده از Wei تعامل دارید نه اتر.

بعدا دکمه های قرمز آنهایی هستند که gas مصرف می کنند تا به اجرای عملکرد هایی بپردازند که ارزش ها را برای بلاک چین ذخیره می کنند.

اگر می بینید که عملکردی مانند checkPlayerExists () در حال خواندن است، باید قرارداد را تغییر دهید تا آن را به صورت constant تنظیم کنید زیرا آن ارزش را برمی گرداند و متغیر های اصلی را تغییر نمی دهد.

پس قبل از تغییر قرارداد، لازم است که قرارداد حال حاضر را از بین ببریم. عملکرد kill را اجرا کنید و قرارداد را تغییر دهید (اگر عملکرد از نوع constant می باشد، لازم به انجام این کار نیست اما به هر حال می توانید قرارداد را از بین ببرید):

function checkPlayerExists(address player) constant returns(bool){

دوباره آن را ایجاد کنید و خواهید دید که دکمه checkPlayerExists حالا آبی است. آن بدین معنی است که gas برای عملکرد مصرف نمی شود.

اتریوم

توجه داشته باشید که bytes32 و string باید علامت نقل قول دوگانه ” داشته باشند تا کار کنند. این مشکل رایجی است که زمان درج کردن یک آدرس اتفاق می افتد زیرا انها نیازمند ارزش های json هستند که دارای علامت نقل قول دوگانه هستند.

عملکرد bet نیازمند این است که شما مقداری اتر را که بزرگتر از بت مینیمم است، بفرستید و یک عدد بین ۱ و ۱۰ انتخاب کنید.

برای تست آن به بخش بالای ستون راست بروید و یک ارزش پولی مانند تصویر زیر ارسال کنید:

اتریوم

آن ارزشی است که به عنوان msg.value در قرارداد موجود خواهد بود. چیزی بزرگتر از minimumBet را بفرستید که شما برای قرارداد خود تنظیم کرده اید و عملکرد را با ارسال number برای شرط بندی اجرا کنید.

اگر آن بیان دارد که gas بیشتر از محدوده می باشد، صفحه را رفرش کرده و طبق آموزش های قبلی کوکی ها را بردارید.

بعد از بازی کردن با عملکرد ها، من متوجه شدم که distributePrizes () همه متغیر های قرارداد را دوباره تنظیم نمی کند، بنابراین totalBet و numberofBets بعد از اولین بازی همان باقی ماندند.

بنابراین من عملکرد جدیدی مانند این ایجاد کردم:

function resetData(){
   players.length = 0; // Delete all the players array
   totalBet = 0;
   numberOfBets = 0;
}

و من آن را از عملکرد distributeprizes () فرا خواندم. من یک مشکل بالقوه را پیدا کردم که قبلا نمی دانستم و آن قدرت تست کردن است.

حالا قبل از رفتن به مرحله بعدی، اجازه دهید قرارداد را کامپایل کنیم تا بعدا آن را با برنامه فرانت اند مورد استفاده قرار دهیم.

فایل 2_deploy_contracts.js در داخل پوشه migrations ایجاد کنید. این فایل جایی است که شما ارزش هایی را تنظیم می کنید که constructor قرارداد شما دریافت خواهد کرد و در مورد من به صورت زیر است:

var Casino = artifacts.require("./Casino.sol");
module.exports = function(deployer) {
  deployer.deploy(web3.toWei(0.1, 'ether'), 100, {gas: 3000000});
};

توضیحات:

ابتدا ما به قرارداد Casino.sol نیاز داریم.

سپس در روش . deploy () بت مینیمم را تعیین می کنیم، در این مورد مقدار آن ۰.۱ اتر است که با ان عملکرد تبدیل به wei شده است.

۱۰۰ بیشترین مقدار بازی کنندگان می باشد.

سرانجام محدوده gas که ما مایل به استفاده برای ایجاد قرارداد هستیم.

بعد از آن به پوشه پروژه خود بروید و فرمان truffle compile را اجرا کنید. این یک فایل json ایجاد می کند که همه داده های مورد نیاز در مورد قرارداد را دارد.

ما از همه این داده ها استفاده نمی کنیم اما شما خودتان می دانید که چه وقتی از چارچوب truffle استفاده می کنید. آن به شما کمک می کند که به جای استفاده از Remix IDE هر چیزی را از رسته فرمان (command line) و ایجاد و تست نمایید.

وقتی که تست را انجام دادید، به ادامه بخش بعدی آموزش بپردازید.

۳- ایجاد برنامه فرانت اند

با باز کردن فایل index.js شروع کنید تا برنامه را با react ایجاد نمایید:

import React from 'react'
import ReactDOM from 'react-dom'
import Web3 from 'web3'
import './../css/index.css'
class App extends React.Component {
   constructor(props){
      super(props)
      this.state = {
         lastWinner: 0,
         timer: 0
      }
   }
voteNumber(number){
      console.log(number)
   }
render(){
      return (
         <div className="main-container">
            <h1>Bet for your best number and win huge amounts of Ether</h1>
<div className="block">
               <h4>Timer:</h4> &nbsp;
               <span ref="timer"> {this.state.timer}</span>
            </div>
<div className="block">
               <h4>Last winner:</h4> &nbsp;
               <span ref="last-winner">{this.state.lastWinner}</span>
            </div>
<hr/>
<h2>Vote for the next number</h2>
            <ul>
               <li onClick={() => {this.voteNumber(1)}}>1</li>
               <li onClick={() => {this.voteNumber(2)}}>2</li>
               <li onClick={() => {this.voteNumber(3)}}>3</li>
               <li onClick={() => {this.voteNumber(4)}}>4</li>
               <li onClick={() => {this.voteNumber(5)}}>5</li>
               <li onClick={() => {this.voteNumber(6)}}>6</li>
               <li onClick={() => {this.voteNumber(7)}}>7</li>
               <li onClick={() => {this.voteNumber(8)}}>8</li>
               <li onClick={() => {this.voteNumber(9)}}>9</li>
               <li onClick={() => {this.voteNumber(10)}}>10</li>
            </ul>
         </div>
      )
   }
}
ReactDOM.render(
   <App />,
   document.querySelector('#root')
)

آن ساختار اصلی تنها با react است، آنچه که من انجام دادم به این صورت بود:

ابتدا من react، reactdom، css و Web3 را وارد کردم زیرا آن چیزی است که ما قصد استفاده از آن را داریم.

سپس من یک مولفه <App/> ایجاد کردم که ما قصد داریم برای ایجاد نمای اصلی سند به کار ببریم.

سرانجام من constructor و روش votenumber (number) را ایجاد کردم که ما از آن برای ارسال رای به قرارداد استفاده خواهیم کرد و در روش render ()، من html اصلی را با وقایع onClick اضافه کردم.

اگر شما در زمینه react تازه کار هستید، توصیه می کنم که فقط کد را دستی کپی کنید یا یک وبسایت html ساده با آن وقایع به صورت جاوا اسکریپت ایجاد کنید.

در اینجا به css توجه نمایید:

body{
    font-family: 'open sans';
    margin: 0;
}
ul{
    list-style-type: none;
    padding-left: 0;
    display: flex;
}
li{
    padding: 40px;
    border: 2px solid rgb(30,134,255);
    margin-right: 5px;
    border-radius: 10px;
    cursor: pointer;
}
li:hover{
    background-color: rgb(30,134,255);
    color: white;
}
li:active{
    opacity: 0.7;
}
*{
   color: #444444;
}
.main-container{
   padding: 20px;
}
.block{
   display: flex;
   align-items: center;
}
.number-selected{
   background-color: rgb(30,134,255);
   color: white;
}
.bet-input{
   padding: 15px;
   border-radius: 10px;
   border: 1px solid lightgrey;
   font-size: 15pt;
   margin: 0 10px;
}

وقتی که ترکیب شد، برنامه مانند زیر به نظر می رسد:

اتریوم

حال اجازه دهید به قرارداد متصل شویم. ما می توانیم از truffle برای ایجاد قرارداد استفاده کنیم و به داده ها دسترسی پیدا کنیم اما به منظور انجام آن، شما باید کل بلاک چین را که چندین گیگابایت است، دانلود نمایید.

از آنجا که من می خواهم این برای همه در دسترس باشد، اجازه دهید تنها قرارداد را با Remix IDE ایجاد کنیم و ABI را با آدرس دریافت کنیم.

به remix.ethereum.org بروید و قرارداد را با کلیک بر روی Create ایجاد کنید.

جزئیات قرارداد را درست در انتها باز کنید:

اتریوم

سپس سطح کاربری ABI را کپی کنید:

اتریوم

سپس به index.js بروید و کد زیر را درست در constructor درج کنید:

constructor(props){
   super(props)
   this.state = {
      lastWinner: 0,
      numberOfBets: 0,
      minimumBet: 0,
      totalBet: 0,
      maxAmountOfBets: 0,
   }
   if(typeof web3 != 'undefined'){
      console.log("Using web3 detected from external source like Metamask")
      this.web3 = new Web3(web3.currentProvider)
   }else{
      this.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
   }
   const MyContract = web3.eth.contract([here goes the ABI interface])
   this.state.ContractInstance = MyContract.at("0x925d81c01d878899adbb7d38f84ce9d5284fa2e7")
}

آنچه که من انجام دادم به این صورت بود:

ابتدا من constructor را ایجاد می کنم و state ابتدایی برنامه را تنظیم می کنم.

سپس بررسی می کنم که آیا متغیر web3 که ما داخل کردیم، تعریف شده است یا نه. Web3 وقتی تعریف می شود که استفاده هایی مانند Metamask به تزریق web3 خود بپردازند. در حقیقت اگر شما از metamask استفاده کنید، خواهید دید که ابزار های توسعه کروم پیام می دهند.

من قرارداد ABI را تنظیم می کنم، بنابراین برنامه می داند که چه عملکرد هایی برای این قرارداد موجود هستند.

سرانجام آدرس قرارداد را با MyContract.at () تنظیم می کنم. من این نمونه را در state به عنوان یک ویژگی بنام ContractInstance ذخیره می کنم اما نکته مهمی برای دانستن این است که MyContract.at () نمونه را برگشت می دهد.

آدرس قرارداد را با کلیک بر روی گزینه زیر در Remix IDE دریافت کنید:

اتریوم

با قرارداد ABI و آدرس آن، شما می توانید از عملکرد های قرارداد در برنامه فرانت اند خود استفاده نمایید.

اجازه دهید فرض کنیم که شما می خواهید عملکرد bet () را اجرا کنید، در آن صورت آنچه که انجام می دهید به صورت زیر خواهد بود:

yourContractInstance.bet(7, {
   gas: 300000,
   from: web3.eth.accounts[0],
   value: web3.toWei(0.1, 'ether')
}, (err, result) => {
   // Result is the transaction address of that function
})

۱- اساسا از نمونه خود استفاده کرده و عملکرد را اجرا کنید.

۲- پارامتر های عملکرد را بفرستید، در مورد من ۷ شماره مورد شرط بندی است.

۳- یک object با عناوین gas: و from: بفرستید. Value: مقدار اتری است که کاربر شرط بندی می کند و سپس شما می توانید در قرارداد با msg.value به آن دسترسی داشته باشید.

۴- سرانجام با err و result فراخوانی کنید. اگر خطایی در کار نباشد error پوچ می شود و result تنها آدرس تراکنش است که شما می توانید در etherscan.io ببینید. از آنها برای نمایش پیام هایی استفاده کنید که توضیح می دهند که بعد از اجرا چه اتفاقی افتاده است.

شما چیزی بسیار شبیه به عملکرد های ثابت و متغیر های عمومی انجام خواهید داد. زیرا آنها برای اجرا نیاز به gas ندارند و شما می توانید آنها را مانند زیر اجرا کنید:

yourContractInstance.maxAmountOfBets((err, result) => {
   if(result != null) // Do something
})

به این ترتیب شما به متغیر عمومی maxAmountofBets می رسید.

حالا من کد فرانت اند را به سبک خودم توسعه داده ام. این کد کامل است:

import React from 'react'
import ReactDOM from 'react-dom'
import Web3 from 'web3'
import './../css/index.css'
class App extends React.Component {
   constructor(props){
      super(props)
      this.state = {
         lastWinner: 0,
         numberOfBets: 0,
         minimumBet: 0,
         totalBet: 0,
         maxAmountOfBets: 0,
      }
if(typeof web3 != 'undefined'){
         console.log("Using web3 detected from external source like Metamask")
         this.web3 = new Web3(web3.currentProvider)
      }else{
         console.log("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
         this.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
      }
const MyContract = web3.eth.contract([{"constant":false,"inputs":[],"name":"generateNumberWinner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"numberOfBets","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"player","type":"address"}],"name":"checkPlayerExists","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"resetData","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"maxBets","type":"uint256"}],"name":"setMaxAmountOfBets","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"}],"name":"bet","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[],"name":"distributePrizes","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"numberWinner","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"minimumBet","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"maxAmountOfBets","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"players","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalBet","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"_minimumBet","type":"uint256"},{"name":"_maxAmountOfBets","type":"uint256"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"}])
      this.state.ContractInstance = MyContract.at("0x7a684de06f473636e03e2d951c78d190eeecb411")
   }
componentDidMount(){
      this.updateState()
      this.setupListeners()
setInterval(this.updateState.bind(this), 10e3)
   }
updateState(){
      this.state.ContractInstance.minimumBet((err, result) => {
         if(result != null){
            this.setState({
               minimumBet: parseFloat(web3.fromWei(result, 'ether'))
            })
         }
      })
      this.state.ContractInstance.totalBet((err, result) => {
         if(result != null){
            this.setState({
               totalBet: parseFloat(web3.fromWei(result, 'ether'))
            })
         }
      })
      this.state.ContractInstance.numberOfBets((err, result) => {
         if(result != null){
            this.setState({
               numberOfBets: parseInt(result)
            })
         }
      })
      this.state.ContractInstance.maxAmountOfBets((err, result) => {
         if(result != null){
            this.setState({
               maxAmountOfBets: parseInt(result)
            })
         }
      })
   }
// Listen for events and executes the voteNumber method
   setupListeners(){
      let liNodes = this.refs.numbers.querySelectorAll('li')
      liNodes.forEach(number => {
         number.addEventListener('click', event => {
            event.target.className = 'number-selected'
            this.voteNumber(parseInt(event.target.innerHTML), done => {
// Remove the other number selected
               for(let i = 0; i < liNodes.length; i++){
                  liNodes[i].className = ''
               }
            })
         })
      })
   }
voteNumber(number, cb){
      let bet = this.refs['ether-bet'].value
if(!bet) bet = 0.1
if(parseFloat(bet) < this.state.minimumBet){
         alert('You must bet more than the minimum')
         cb()
      } else {
         this.state.ContractInstance.bet(number, {
            gas: 300000,
            from: web3.eth.accounts[0],
            value: web3.toWei(bet, 'ether')
         }, (err, result) => {
            cb()
         })
      }
   }
render(){
      return (
         <div className="main-container">
            <h1>Bet for your best number and win huge amounts of Ether</h1>
<div className="block">
               <b>Number of bets:</b> &nbsp;
               <span>{this.state.numberOfBets}</span>
            </div>
<div className="block">
               <b>Last number winner:</b> &nbsp;
               <span>{this.state.lastWinner}</span>
            </div>
<div className="block">
               <b>Total ether bet:</b> &nbsp;
               <span>{this.state.totalBet} ether</span>
            </div>
<div className="block">
               <b>Minimum bet:</b> &nbsp;
               <span>{this.state.minimumBet} ether</span>
            </div>
<div className="block">
               <b>Max amount of bets:</b> &nbsp;
               <span>{this.state.maxAmountOfBets} ether</span>
            </div>
<hr/>
<h2>Vote for the next number</h2>
<label>
               <b>How much Ether do you want to bet? <input className="bet-input" ref="ether-bet" type="number" placeholder={this.state.minimumBet}/></b> ether
               <br/>
            </label>
   <ul ref="numbers">
               <li>1</li>
               <li>2</li>
               <li>3</li>
               <li>4</li>
               <li>5</li>
               <li>6</li>
               <li>7</li>
               <li>8</li>
               <li>9</li>
               <li>10</li>
            </ul>
         </div>
      )
   }
}
ReactDOM.render(
   <App />,
   document.querySelector('#root')
)

من وضعیت برنامه را در ابتدا به روز رسانی می کنم، در این زمان مولفه آماده تنظیم متغیر های مهمی مانند کل بت اتر می شود.

سپس من click event listeners را برای هر شماره <1i> تنظیم می کنم، بنابراین وقتی که بر آن کلیک می شود، آن عملکرد bet () قرارداد را با عدد صحیح و مقدار اتری که می خواهید شرط بندی کنید، اجرا می کند.

حال مانند زیر کار می کند و چنین سیمایی دارد:

اتریوم

کد کامل در github من به آدرس https://github.com/merlox/casino-ethereum موجود است.

اگر تا به حال کار را به درستی انجام داده اید، به شما تبریک می گویم. تعداد کمی از مردم کل محتوا را می خوانند، شما یکی از آنها خواهید بود؟

۴- از بین بردن آنلاین برنامه با IPFS

حالا که برنامه کامل است، زمان انتقال آن به جهان آنلاین با هاستینگ رایگان همیشگی و غیر متمرکز است، چقدر این موضوع به نظر شما عالی است؟

آن قدرت IPFS است.

خوب، در واقعیت این موضوع خیلی هم خوب نیست زیرا برنامه شما تا زمانی که نود آن را آنلاین نگه دارد، آنلاین می ماند. آن مانند فیلم های تورنت همتا به همتا می باشد و می توانید به دانلود آن بپردازید تا زمانی که فردی در حال سید کردن آن است.

اما آن کاملا غیر متمرکز خواهد ماند و بنابراین ارزش انجام دادنش را دارد.

بیا با نصب IPFS شروع کنیم، به وبسایت انها بروید و برنامه را نصب کنید: https://ipfs.io/docs/install/

در پنجره، من مجبور بودم که پوشه را متغیر path اضافه کنم تا قادر به اجرای فرمان ipfs از هر جایی باشم. اگر شما در حال خواندن این هستید، شما باید از قبل بدانید که چگونه به تغییر متغیر مسیر (path) می پردازید.

بعد از نصب آن، به دسکتاپ خود بروید و فرمان های زیر را اجرا کنید:

ipfs daemon

این یک نود را ایجاد خواهد کرد. در یک رسته فرمان (command line) یا ترمینال (terminal) دیگر انجام می شود:

ipfs swarm peers

این شما را به همتا هایی خواهد رساند که محتوای شما را به اشتراک می گذارند.

ipfs add -r dist/

این پوشه dist شما را به شبکه اضافه خواهد کرد. می بینید که یک هش طولانی برای شما ایجاد شده است. آخرین هش یک شناسایی کننده بی نظیر برای آن فولدر است:

added Qmc9HzLPur2ncuUArLjAaa4t2HrXFycgjUPb6122N6tzi2 dist/build.js
added QmZBaGYWsACJ5aCFhW459xHZ8hk4YazX1EQFiSenu3ANfR dist/index.html
added QmfZoCnPcgmHYmFJqHBcyuFh3FEYrTZqGdGyioSMrAZzw2 dist

آخرین هش را کپی و اجرا کنید:

ipfs name publish QmfZoCnPcgmHYmFJqHBcyuFh3FEYrTZqGdGyioSMrAZzw2

به چیزی مانند زیر خواهید رسید:

Published to QmRDVed784YwKrYAgiiBbh2rFGfCUemXWk3NkD7nWdstER: /ipfs/QmfZoCnPcgmHYmFJqHBcyuFh3FEYrTZqGdGyioSMrAZzw2

این یعنی که محتوای شما در آن url موجود است. می توانید آن را با رفتن به gateway.ipfs.io/ipns/<your-hash-here> بررسی کنید.

مورد من به صورت زیر می باشد:

gateway.ipfs.io/ipns/QmRDVed784YwKrYAgiiBbh2rFGfCUemXWk3NkD7nWdstER

بارگذاری مدتی طول می کشد زیرا در حال حاضر شبکه خیلی بزرگ نیست. سپس برنامه غیر متمرکز محشر خود را خواهید دید. به یاد داشته باشید که metamask را در Ropsten Test Network تنظیم کنید.

اگر خواستار تغییر دادن فایل های خود بودید، به یاد داشته باشید که wepack و سپس ipfs add _r dist و ipfs name publish <the-hash> را اجرا کنید. متوجه می شوید که هش نام عمومی همیشه یکی است.

برنامه غیر متمرکز شما حالا آنلاین است.

۵- از یک دامنه سفارشی برای برنامه استفاده کنید

حالا شما ممکن است بخواهید که از دامنه خودتان استفاده کنید زیرا کمی غیر حرفه ای است که از این url برای پروژه های خود استفاده کنید.

این نمونه آموزشی عالی توسط IPFS جزئیات کامل را به وضوح برای شما شرح می دهد.

من برای شما توضیح می دهم که چگونه آن را با godaddy انجام دهید. دیگر فراهم کنندگان این سرویس هم تقریبا به همین شیوه عمل کنند.

۱- ابتدا به مدیریت DNS دامنه خود در godaddy بروید.

۲- یک سابقه جدید TXT با Host:@ و ارزش dnslink=/ipns/<your-hash> اضافه نمایید.

3- آن را ذخیره و تغییر دهید یا یک سابقه A را با Host:@ Pointing to: ۲۱۷.۱۸۲.۱۹۵.۲۳ اضافه نمایید. آن آی پی اورجینال gateway.ipfs.io می باشد.

۴- هر کدام از سابقه های دیگر A را بردارید زیرا ممکن است با این سابقه جدید A تداخل داشته باشند.

۵- آن را ذخیره کنید و بعد از چند دقیقه به دامنه بروید و بررسی کنید که آیا آن کار می کند.

۶- همچنین می توانید با رفتن به gateway.ipfs.io/ipns/<yourdomain> به آن دسترسی داشته باشید.

۷- در این مورد، url ها gateway.ipfs.io/pepo.es و pepo.es هستند.

اتریوم

۶- بازی با برنامه غیر متمرکز نهایی

این کل مواد آموزشی بود. اگر تا آخر آن را خوانده اید، باید به شما تبریک گفت. شما عالی هستید و حالا قادر به ایجاد برنامه های غیر قابل توقف هستید، همچنان که صفحه ethereum.org ادعا می کند.

از تلاش خود لذت ببرید و مطمئن شوید که برنامه را تا آنجا که می توانید برای پیدا کردن هر گونه عیبی تست کنید. اگر به اندازه کافی شجاع باشید، می توانید آن را درشبکه Homestead اصلی ایجاد کنید.

تنها تفاوت این است که شما باید وقتی که در حال ایجاد برنامه هستید، شبکه Metamask خود را تغییر دهید.

همچنین من به شما توصیه می کنم که Solidity Docs، Web3.js Docs، Truffle Docs و IPFS Docs را بررسی نمایید. آنها همه چیز هایی که شما برای یادگیری بیشتر نیاز دارید را دارند و برنامه های خیلی بزرگی را ایجاد می کنند.

شما می توانید یاد بگیرید که سریعتر از truffle و برنامه های توسعه استفاده کنید زیرا تنها می توانید آنها را از طریق command line ایجاد کنید، البته لازم است که کل بلاک چین را برای این کار دانلود نمایید.

همچنین شما می توانید اعداد واقعا تصادفی را با قرارداد های Oraclize ایجاد نمایید. اگر علاقه مند به تبدیل برنامه غیر متمرکز خود به یک برنامه حرفه ای هستید، در مورد چنین مواردی بررسی کنید.

و در نهایت اگر ابهامی در چیزی وجود دارد و همچنین برای خطا های احتمالی و سوالاتی در مورد پروژه می توانید کامنت های خود را با مطرح کنید.

منبع
medium

نوشته های مشابه

3 دیدگاه
جدید ترین
قدیمی ترین محبوب ترین
Inline Feedbacks
View all comments
دکمه بازگشت به بالا