1. 首页
  2. 实战操作

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

在本教程中,我的目标是引导您完成从头到尾创建分散式应用程序所需的步骤,并将其部署在以太坊中。

 

注意:已于3月2日更新并修订,以修复多项重大更改。

以下是它的外观:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

我正在制作它,因为我看到很多教程并没有真正向您展示如何连接所有内容并拥有最终的dapp,包括分散托管和IPFS,这是一项强大的技术。

另外,因为我发现自己迷失了,我不确切知道如何开发以太坊网站谈论的真正无法阻挡的应用程序

您将学习一些基本和复杂的主题,例如:

  • 如何从头开始创建智能合约
  • 如何在ropsten上部署智能合约
  • 如何创建Dapp的前端
  • 如何将已部署的合同与您的应用程序连接
  • 如何在分散的IPFS托管上部署最终的dapp
  • 如何在IPFS中使用自定义域

在本教程结束时,您将拥有一个完全分散的网站,使用您自己的智能合约,以便您可以在浏览器中使用它们并使用Metamask或Mist与区块链进行交互。

您必须了解区块链,已创建的软件,合同如何工作的基本知识以及使用javascript编程网站的经验。

本教程非常适合您,如果您是“我知道它是如何工作的,我只想开始在以太坊中创建一些东西来学习并希望成为Dapp开发人员”的Web开发人员

顺便说一句,我写了一本关于以太坊开发的书,从头开始学习可靠性,你将学习最快成为以太坊开发者的最重要的概念。在这里查看,免费为kindle无限制我们:https//www.amazon.com/dp/B07BJB1YNQ

您也可以在我的网站上以20美元的PDF格式获取它:https//merunas.io/book

以下是我们将要使用的技术:

  • 数据库:以太坊的Testnet Ropsten区块链。
  • 托管:IPFS在分散的平台上永远免费托管。
  • 前端:与webpack的React.js。别担心我会解释最重要的步骤。你可以使用你喜欢的任何框架或简单的JavaScript。
  • 域名:Godaddy。在这里,我可以使用像peername这样的分散域名服务,但是对于godaddy或任何其他域名注册商来说,它更快更容易。
  • 合同的编程语言:Solidity 0.4.11,现在是开发智能合约的最着名的语言。
  • 前端合同:web3.js在您的Web用户界面中使用这些合同。
  • 框架:松露部署,测试和编译我们的合同。
  • 开发服务器:Node.js在本地开发时使用应用程序以及testrpc。
  • Metamask:像最终用户一样使用最终应用程序。

这些是我们要遵循的步骤:

  1. 设置项目
  2. 编制Solidity合同
  3. 创建前端应用程序
  4. 使用IPFS在线部署应用程序
  5. 为应用程序使用自定义域
  6. 玩最后的Dapp!

1.设置项目

我将向您解释我们将要创建的内容,以便您对最终结果有清晰的图像。

我们将创建一个类似于应用程序的赌场,用户可以在1到10之间的数字下注金钱,如果他们是正确的,他们将赢得所有以下100美元赌注之后的以太币资金的一部分。

这是一个相当复杂的应用程序,但你会喜欢它,因为它是原始的,有趣的使用。

现在我必须警告你,这不是一个安全的应用程序,因为你不能简单地用Solidity生成随机性。你需要在区块链上尝试不同的随机生成技术,看看哪些有效。

以本教程为导言,了解从头到尾创建Dapp的整个过程。

首先,如果您还没有nodejs,请从他们的网站nodejs.org下载当前版本的node.js. 然后,创建一个casino-ethereum在计算机桌面上调用的文件夹。在里面,打开终端或命令行并执行命令:npm init -y

之后:

npm i -D -g truffle

在此项目中将以太坊开发框架Truffle安装为开发依赖项(-D)和全局(-g)。

然后执行:

npm init -y 
truffle init

这两个命令将通过为dapp开发创建基本文件来启动项目。

之后,做:

npm i -D webpack react react-dom babel-core babel-loader babel-preset-react babel-preset-env css-loader style-loader json-loader web3@0.20.0

这些是我们创建dapp前端所需的所有工具。有很多依赖项,因为我们将使用最新版本的javascript和React.js创建一个Web应用程序。

主要是我们安装了webpack,react,babel和web3。

请注意,我们正在安装web3的0.20.0版本,web3@0.20.0因为最新版本的web3,1.0仍然处于测试阶段且不稳定。

然后,安装npm i -g http-server。这是一个轻量级服务器,您可以在开发时使用它来本地托管您的Web应用程序localhost:8080

现在,您拥有使用react和webpack创建此项目所需的所有依赖项。

之后,转到项目文件夹并创建一个webpack.config.js文件。这个文件将我们的所有javascripts和css组合在一起生成一个文件,调用build.js所有js代码转换为与新旧浏览器兼容。这样我们就可以使用最新的javacript的尚未发布的功能。

现在在文件中写下这段代码:

const path = require('path')
module.exports = { 
   entry:path.join(__ dirname,'src / js','index.js'),//我们的前端将在src文件夹
   输出中:{ 
      path:path.join(__ dirname,'dist' ),
      filename:'build.js'//最终文件将在dist / build.js 
   }中创建,
   模块:{ 
      loaders:[{ 
         test:/\.css $ /,//加载css in react 
         use :[ '风格装载机', 'CSS-装载机'],
         包括:/ SRC / 
      },{ 
         测试:/\.jsx?$/,//要加载js和JSX文件
         装载机: '巴贝尔装载机',
         exclude:/ node_modules /,
         query:{ 
            presets:['es2015','react','stage-2'] 
         }
      },{ 
         test:/\。json $ /,//加载json文件
         加载器:'json-loader' 
      }] 
   } 
}

之后,src/在项目中和该文件夹内创建文件夹,创建文件夹js/css/组织源代码。

在每个文件夹内创建index.jsindex.css 。

最后dist/在外层和内层创建文件夹index.html 。

结构如下:

合同/ 
-  Migrations.sol 
迁移/ 
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>反应代码的插入位置以及<script src="build.js"></script>包含webpack生成的构建文件。

创建文件contracts/Casino.sol ,这是我们将在下一节编写的主要Solidity合同。

准备好这一切后,我们可以开始创建应用程序了!


2.编制Solidity合同

在本节中,您将编写项目的主要合同,并且您将对其进行测试,以确定它是否正常工作,以便不部署可能使某人损失真钱的易受攻击的代码。

首先,每个Solidity契约必须以编译器版本开头,因此请在Casino.sol您创建的文件的顶部写下:

pragma solidity 0.4.20;

现在创建合同:

pragma solidity 0.4.20;
合约赌场{ 
   地址公共所有者;
   function Casino()public { 
      owner = msg.sender; 
   }
   function kill()public { 
      if(msg.sender == owner)selfdestruct(owner); 
   } 
}
  • 名为owner的地址变量是Metamask帐户中的长字符串,在我的例子中是0x08f96d0f5C9086d7f6b59F9310532BdDCcF536e2。
  • function Casino()是构造函数,因为它与合同具有相同的名称,我们使用它来设置该合同的所有者。创建此合同的用户地址是所有者。
  • function kill()使用时要销毁的合同。当然只有主人可以杀死它。合同存储的剩余以太将被发送到所有者的地址。只有在合同被某些黑客入侵而你无法获得保护的情况下才能使用它。

请记住,目标是:

创建一个类似于应用程序的赌场,用户可以在1到10之间的数字下注,如果他们是正确的,他们将赢得100个赌注后所有以太币的一部分。

所以我们必须完成以下任务:

  • 记录用户为什么号码下注多少。
  • 投注的最低价格。
  • 容纳的乙醚总量。
  • 存储有多少投注的变量。
  • 何时停止下注并显示中奖号码。
  • 为每位获胜者发送获胜金额的函数。

这些是功能:

  • 打赌一个号码。
  • 生成随机数作为赢家。
  • 将以太送给获奖者。

记录用户为什么号码下注多少。

我们可以使用struct带有一种mapping变量的类型来存储用户的地址,下注金额和javascript类对象中的数字。

struct就像是javascript中的一个对象,mapping就像一个数组。

我们来看看代码:

pragma solidity 0.4.20; 
合约赌场{ 
   地址公共所有者; 
   uint256 public minimumBet; 
   uint256 public totalBet; 
   uint256 public  numberOfBets; 
   uint256 public maxAmountOfBets = 100; 
   地址[]公共参与者;
   struct Player { 
      uint256 amountBet; 
      uint256 numberSelected; 
   }
   //播放器的地址和=>用户信息    
   映射(address => Player)public playerInfo;
   function Casino()public { 
      owner = msg.sender; 
   } 
   功能杀灭(){公共
      如果(msg.sender ==所有者)自毁(所有者); 
   } 
}

struct命名播放器是由下注的金额和选择的数量,因为我们希望保持的用户多少醚具有下注,什么号的轨道。

然后我们创建一个名为playerInfo的映射来执行类似这样的操作playerInfo[here_goes_his_address].amountBet,我们可以将地址用作javascript对象中的“键”来访问它的属性。

我们希望有一系列玩家知道谁在玩游戏,以便将奖励分配给获胜者。

修改构造函数以定义游戏的最小赌注,以便您可以选择可变数量的最小赌注来分发奖品:

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

现在在合约结束时创建一个函数来下注1到10之间的数字,包括:

pragma solidity 0.4.20; 
合约赌场{ 
   地址公共所有者; 
   uint256 public minimumBet; 
   uint256 public totalBet; 
   uint256 public numberOfBets; 
   uint256 public maxAmountOfBets = 100; 
   地址[]公共参与者;
   struct Player { 
      uint256 amountBet; 
      uint256 numberSelected; 
   }
   //播放器的地址和=>用户信息    
   映射(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); 
   }
   //投注1到10之间的数字,包括
   函数投注(uint256 numberSelected)公共应付{ 
      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; 
   }
 }
  • 这个词payablemodifier用来表示当你执行它时这个函数可以接收以太的。
  • require()函数就像一个if必须返回true 的语句。如果require中的条件评估为false,则该函数将停止,并且以太付款将恢复为发件人。我们使用它来验证玩家是否已经玩过,该数字在范围内并且所支付的赌注大于或等于最小赌注。
  • msg.sender并且msg.value是用户在执行合同时定义的值。发件人是他的地址,值是他在执行此payable功能时支付的以太数量。
  • 然后我们定义该用户的赌注金额playerInfo[msg.sender].amountBet = msg.sender;msg.sender是执行该功能的用户的地址。
  • 然后我们增加numberOfBets1.这个变量是一个计数器,用于查看当有100并分配奖励时,此游戏有多少赌注可以阻止它。
  • 最后,我们增加了此游戏的totalAmount赌注,该赌注将分散在获胜者身上。

您可以在该函数的第一个require语句中看到:

需要(!checkPlayerExists(msg.sender));

我正在调用一个函数checkPlayerExists()来检查用户是否已经玩过,因为我们只希望每个人每场只玩一次。功能还没有。那么现在让我们创建它:

pragma solidity 0.4.20; 
合约赌场{ 
   地址公共所有者; 
   uint256 public minimumBet; 
   uint256 public totalBet; 
   uint256 public numberOfBets; 
   uint256 public maxAmountOfBets = 100; 
   地址[]公共参与者;
   struct Player { 
      uint256 amountBet; 
      uint256 numberSelected; 
   }
   //播放器的地址和=>用户信息    
   映射(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; 
   }
   //投注1到10之间的数字,包括
   函数投注(uint256 numberSelected)公共应付{ 
      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函数的关键字表示这是一个不需要运行任何气体的函数,因为它从区块链返回已存在的值。它正在读取一个值。这就是为什么它是免费的。

创建这些很酷的功能后,我们必须检查下注数量是否大于或等于最大值,以便我们可以生成获胜者数量。因为当有99个投注时,我们希望下一个投注,即投注数100,释放价格并执行所需的计算。得到它?

太棒bet()了,为此目的在函数的末尾添加这个:

if(numberOfBets> = maxAmountOfBets)generateNumberWinner();

现在我们必须创建一个函数generateNumberWinner(),它将随机生成1到10之间的数字来决定胜利者:

pragma solidity 0.4.20; 
合约赌场{ 
   地址公共所有者; 
   uint256 public minimumBet; 
   uint256 public totalBet; 
   uint256 public numberOfBets; 
   uint256 public maxAmountOfBets = 100; 
   地址[]公共参与者;
   struct Player { 
      uint256 amountBet; 
      uint256 numberSelected; 
   }
   //播放器的地址和=>用户信息    
   映射(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; 
   }
   //投注1到10之间的数字,包括
   函数投注(uint256 numberSelected)公共应付{ 
      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; 
   }
   //生成1到10之间的数字,它将成为胜利者
   函数generateNumberWinner()public { 
      uint256 numberGenerated = block.number%10 + 1; //这不是安全的
      distributePrizes(numberGenerated); 
   }
 }

它取当前块号并得到最后一个数字+ 1,所以如果块号是128142,则生成的数字将是128142%10 = 2和2 +1 = 3。

这是不安全的,因为根据条件很容易知道哪个数字将成为赢家。矿工可以决定查看区号以获取自己的利益。

然后,我们通过以下功能为获奖者分发奖品distributePrices(numberGenerated) :

pragma solidity 0.4.20; 
合约赌场{ 
   地址公共所有者; 
   uint256 public minimumBet; 
   uint256 public totalBet; 
   uint256 public numberOfBets; 
   uint256 public maxAmountOfBets = 100; 
   地址[]公共参与者;
   struct Player { 
      uint256 amountBet; 
      uint256 numberSelected; 
   }
   //播放器的地址和=>用户信息    
   映射(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; 
   }
   //投注1到10之间的数字,包括
   函数投注(uint256 numberSelected)公共应付{ 
      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; 
   }
   //生成1到10之间的数字,它将成为胜利者
   函数generateNumberWinner()public { 
      uint256 numberGenerated = block.number%10 + 1; //这不是安全的
      distributePrizes(numberGenerated); 
   }
   //根据总赌注
   函数distributePrizes(uint256 numberWinner)public { 
      address [100]记忆赢家发送相应的以太网给每个获胜者; //我们必须创建一个临时的内存数组,其大小固定为
      uint256 count = 0; //这是获奖者数量的计数
      for(uint256 i = 0; i <players.length; i ++){ 
         address playerAddress = players [i]; 
         if(playerInfo [playerAddress] .numberSelected == numberWinner){ 
            winners [count] = playerAddress; 
            计数++; 
         } 
         delete playerInfo [playerAddress]; //删除所有玩家
      }
      players.length = 0; //删除所有玩家阵列
      uint256 winnerEtherAmount = totalBet / winners.length; //每位获胜者获得多少奖金
      for(uint256 j = 0; j <count; j ++){ 
         if(winners [j]!= address(0))//检查此固定数组中的地址是否为空
         winners [j] .transfer(winnerEtherAmount); 
      } 
   }
 }

此功能执行以下操作:

  1. 首先winners通过检查玩家是否numberSelected是一个来生成一系列获胜者numberWinner 。该winners数组是一个memory在函数执行后被删除的数组。它必须具有固定的大小。
  2. 然后它根据总赌注金额和获胜者数量计算每个获胜者获得的以太币数量。赢家越少,奖金就越大。
  3. 然后它为每个获胜者发送相应数量的以太winners[j].transfer 。

这应该是它。现在我们有一份工作合同,允许人们作为一个简单的赌场玩。

我要做的最后一件事是创建一个匿名fallback function函数,一个没有名称的函数,payable当你将ether发送给契约而不执行任何函数时,它具有执行的修饰符:

//后备功能,以防有人向合同发送以太,这样就不会丢失,并增加将在每个游戏
   函数()公共支付{}中分配的合同的库。

这样您就可以保存发送给合同的以太币。否则会被拒绝。

这是您刚刚创建的完整合约:

pragma solidity 0.4.20; 
合约赌场{ 
   地址公共所有者; 
   uint256 public minimumBet; 
   uint256 public totalBet; 
   uint256 public numberOfBets; 
   uint256 public maxAmountOfBets = 100; 
   地址[]公共参与者;
   struct Player { 
      uint256 amountBet; 
      uint256 numberSelected; 
   }
   //播放器的地址和=>用户信息    
   映射(address => Player)public playerInfo;
   function()public paid {}
   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; 
   }
   //投注1到10之间的数字,包括
   函数投注(uint256 numberSelected)公共应付{ 
      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; 
   }
   //生成1到10之间的数字,它将成为胜利者
   函数generateNumberWinner()public { 
      uint256 numberGenerated = block.number%10 + 1; //这不是安全的
      distributePrizes(numberGenerated); 
   }
   //根据总赌注
   函数distributePrizes(uint256 numberWinner)public { 
      address [100]记忆赢家发送相应的以太网给每个获胜者; //我们必须创建一个临时的内存数组,其大小固定为
      uint256 count = 0; //这是获奖者数量的计数
      for(uint256 i = 0; i <players.length; i ++){ 
         address playerAddress = players [i]; 
         if(playerInfo [playerAddress] .numberSelected == numberWinner){ 
            winners [count] = playerAddress; 
            计数++; 
         } 
         delete playerInfo [playerAddress]; //删除所有玩家
      }
      players.length = 0; //删除所有玩家阵列
      uint256 winnerEtherAmount = totalBet / winners.length; //每位获胜者获得多少奖金
      for(uint256 j = 0; j <count; j ++){ 
         if(winners [j]!= address(0))//检查此固定数组中的地址是否为空
         winners [j] .transfer(winnerEtherAmount); 
      } 
   }
 }

你可以看到在我的GitHub上的完整和更新合同https://github.com/merlox/casino-ethereum里面contracts的文件夹。它是一个高级版本,具有用于安全随机数生成的oraclize。

现在合同已经完成,我们有两个选择:

  • 使用松露编写测试并testrpc在这些测试期间部署合同。
  • 使用Solidity的IDE https://remix.ethereum.org手动测试合同。这是编写,测试和部署合同的官方工具。

因为我不想让这篇文章太长,所以我们将使用remix IDE快速测试合同,尽管你必须使用松露进行测试,以确保你的应用程序按预期工作,并且每次更新时都是安全的。

在继续之前,您必须从该链接下载metamask.io。它是用于在浏览器和以太坊钱包上执行智能合约的工具。

安装后,打开它,按照步骤创建一个帐户。请务必保存12个字密码,因为如果您忘记密码或私钥,这是恢复帐户的唯一方法。你应该看到这样的东西:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
嘿! 那就是左边的我

您现在拥有元掩码中的帐户,您可以使用它与dapps和智能合约进行交互。

所以转到Remix IDE并粘贴合同:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
这是混音ide

这是我们在remix IDE中要做的事情:

  • 将合同部署到Testnet Ropsten区块链,这是区块链,其中ether没有实际价值。
  • 使用metamask来支付每个函数的气体。
  • 确保所有功能按预期工作。

你有2个面板,左边是你的代码,右边是你可以用合同做有趣的东西。

在右侧面板中,单击“运行”选项卡:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

您应该看到有一个红色按钮,Create其中包含一个带占位符的输入字段uint256 _minimumBet 。这是构造函数的最小赌注。你可以把它留空或写一个数字。

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

现在打开Metamask扩展,将网络更改为Ropsten Testnet网络,就像在图像中一样。

接下来转到https://faucet.metamask.io/并请求1免费以太。水龙头是一个可以为您的Ropsten帐户获得以太的地方。

过了一会儿,你会发现你的帐户中有一些不错的以太。

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

转到Remix IDE并确保您在环境中:注入web3:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

这意味着您正在使用从Metamask注入的区块链。现在点击Create部署你的合同,你会看到metamask的一个通知窗口:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

只需点击Submit支付部署费用即可完成,您的合同将在Ropsten网络上生效。

如果由于某种原因,您无法更改环境或未看到通知窗口,请按F12键以打开Chrome开发工具。单击并按住chrome中的重新加载按钮,然后选择最后一个选项:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
清空缓存并重新加载页面

那应该修复那些错误。

在部署合同后,您将在右侧面板上看到合同的地址,功能和更有趣的数据。

像这样的东西:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

在顶部,您有一个按钮,表示Copy address您可以使用该按钮保存该合同的地址供以后使用。只需单击它并将地址保存在某处。

然后,蓝色按钮是constantpublic功能和变量。这意味着当您声明一个公共变量时,uint256 public totalBet;您可以随时访问它的值,因为它是公共的。

你可以说它有一个自动生成的getter方法。

然后constant函数是那些返回值而不修改契约状态的函数。这意味着他们不会修改合同的变量,只是执行并给你一个结果。

让我们从逐个执行功能开始。

如果你点击,minimumBet你会立即获得该变量的值,因为它是公开的:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
在我的情况下,这是因为我在创建合同时在构造函数中设置了它

它返回uint256: 1000000000000000Wei,即0.1以太。请记住,你使用魏而不是以太互动。

接下来,红色按钮是消耗气体以执行将值保存到区块链的功能的按钮。

如果您看到类似函数checkPlayerExists()处于读取状态,则必须更改合同以设置它,constant因为它返回一个值并且不会修改主变量。

因此,在更改合同之前,我们必须销毁当前的合同。执行该kill函数并修改合同(如果函数已经是常量,则不需要执行此操作,但无论如何都可以尝试终止合同):

function checkPlayerExists(地址播放器)常量返回(bool){

再次部署它,你会看到checkPlayerExists按钮现在是蓝色的。这意味着它不会消耗气体来运行。

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
该函数是常量,不需要执行气体并返回布尔值

请注意,bytes32string必须在双引号"工作。这是粘贴地址时发生的常见问题,因为它们需要json值,这是双引号。

bet功能要求您发送的乙醚量大于最小赌注,并选择1到10之间的数字。

要测试它,请转到右列的顶部,并发送货币值,如图中所示:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
100 finney是0.1以太,由于精度问题,你不能写出像0.1这样的浮点值,所以我们使用一个较小的单位

这是msg.value合同中可用的价值。发送大于minimumBet您为合同设置的内容,并通过发送number下注来执行该功能。

如果它说气体超过气体限制,请刷新页面,删除我之前教过的饼干。

在玩完这些功能之后,我意识到该distributePrizes()功能并没有重置合同的所有变量,所以在第一场比赛之后totalBet和之前numberOfBets保持不变。

所以我创建了一个这样的新函数:

function resetData(){ 
   players.length = 0; //删除所有玩家阵列
   totalBet = 0; 
   numberOfBets = 0; 
}

我从distributePrizes()功能中调用它。我发现了一个我以前不知道的潜在问题,那就是测试的力量!

现在在进入下一部分之前,让我们编译合同,以便稍后在前端应用程序中使用它。

创建文件2_deploy_contracts.js里面migrations的文件夹。在此文件中,您可以设置合同构造函数将接收的值,在我的情况下,它是:

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

说明:

  • 首先,我们要求Casino.sol合同。
  • 然后,在。deploy()方法我们指定最小赌注,在这种情况下,它0.1 ether转换为wei使用该函数。
  • 100是玩家的最大数量。
  • 最后是我们愿意用来部署合同的天然气限额。

之后,转到项目文件夹并执行命令truffle compile 。这将生成一个json文件,其中包含有关合同的所有必需数据。

我们不会使用这些数据,只是让您知道何时使用松露框架。它可以帮助您从命令行部署和测试所有内容,而不是使用Remix IDE。

完成测试后,继续学习本教程的下一部分。


3.创建前端应用程序

首先打开index.js文件以使用react创建应用程序:

import来自'react'的React 
导入来自'react-dom' 
的ReactDOM 
导入来自'web3'import './../css/index.css'的Web3
class App扩展了React.Component { 
   constructor(props){ 
      super(props)
      this.state = { 
         lastWinner:0,
         timer:0 
      } 
   }
voteNumber(number){ 
      console.log(number)
   }
render(){ 
      return(
         <div className =“main-container”> 
            <h1>打赌你最好的数字并赢得大量的以太</ h1>
<div className =“block”> 
               <h4>计时器:</ h4>&nbsp; 
               <span ref =“timer”> {this.state.timer} </ span> 
            </ div>
<div className =“block”> 
               <h4>最后一位获胜者:</ h4>&nbsp; 
               <span ref =“last-winner”> {this.state.lastWinner} </ span> 
            </ div>
<HR />
<h2>投票给下一个号码</ 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 
   ')

这只是反应的主要结构,这就是我所做的:

  • 首先我导入了反应,反应,css和web3,因为这就是我们要使用的东西。
  • 然后我创建了一个<App />组件,我们将用它来生成文档的主视图。
  • 最后,我生成了构造函数,voteNumber(number)我们将用于向合同发送投票的方法,并且在render()方法中我添加了基本的html和onClick事件。

如果您是新手,我建议您手动复制代码或使用javascript创建一个包含这些事件的简单html网站。

这是css:

body { 
    font-family:'open sans'; 
    保证金:0; 
} 
ul { 
    list-style-type:none; 
    padding-left:0; 
    显示:flex; 
} 
li { 
    padding:40px; 
    边界:2px solid rgb(30,134,255); 
    保证金权利:5px; 
    border-radius:10px; 
    游标:指针; 
} 
li:hover { 
    background-color:rgb(30,134,255); 
    白颜色; 
} 
li:active { 
    opacity:0.7; 
} 
* { 
   color:#444444; 
} 
.main-container { 
   padding:20px; 
} 
.block { 
   display:flex; 
   align-items:center;
} 
.nu​​mber-selected { 
   background-color:rgb(30,134,255); 
   白颜色; 
} 
.bet-input { 
   padding:15px; 
   border-radius:10px; 
   边框:1px solid lightgrey; 
   font-size:15pt; 
   保证金:0 10px; 
}

合并后,该应用程序如下所示:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

现在让我们连接到合同。我们可以使用松露来部署合同并获取数据,但为了做到这一点,你必须下载几十GB的整个区块链。

因为我希望每个人都可以访问它,所以我们只需将合同部署到Remix IDE并使用Address获取ABI。

转到remix.ethereum.org并单击合同部署Create 。

最后打开合同细节:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

接下来复制ABI接口:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
双击右侧复制这个巨大的代码

然后转到您index.js并在构造函数中粘贴以下内容:

构造函数(props){ 
   super(props)
   this.state = { 
      lastWinner:0,
      numberOfBets:0,
      minimumBet:0,
      totalBet:0,
      maxAmountOfBets:0,
   }
   if(typeof web3!='undefined'){ 
      console.log(“使用从Metamask等外部源检测到web3”)
      this.web3 = new Web3(web3.currentProvider)
   } else { 
      this.web3 = new Web3(new Web3。 providers.HttpProvider(“ http:// localhost:8545 ”))
   }
   const MyContract = web3.eth.contract([这里是ABI接口])
   this.state.ContractInstance = MyContract.at(“0x925d81c01d878899adbb7d38f84ce9d5284fa2e7”)
}

这是我做的:

  • 首先,我创建构造函数并设置state应用程序的初始值。
  • 然后我检查Web3我们导入的变量是否已定义。当Metamask等实用程序注入web3时定义了Web3。事实上,如果您使用元掩码,您将在chrome dev工具中看到该消息。
  • 我设置合同ABI,以便应用程序知道此合同可用的功能。
  • 最后我设置了合同的地址MyContract.at() 。我将实例保存在state一个被调用的属性中,ContractInstance但重要的是要知道MyContract.at()返回实例。

在Remix IDE中单击以下内容获取合同的地址:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

通过合同的ABI及其地址,您可以在前端应用程序中使用合同的功能。

假设您要执行该函数bet() ,在这种情况下您将执行:

yourContractInstance.bet(7,{ 
   gas:300000,
   from:web3.eth.accounts [0],
   value:web3.toWei(0.1,'ether')
},(err,result)=> { 
   //结果是交易该功能的地址
})
  1. 基本上采取您的实例并执行该功能。
  2. 发送函数的参数,在我的情况下7是要下注的数字。
  3. gas:和发送一个对象from: 。这value:只是用户下注的以太网量,以便您可以msg.value在合同中访问它。
  4. 最后用err和回调result 。如果没有错误,则错误为null,结果只是事务地址,因此您可以在etherscan.io中看到它是如何进行调用的。使用它们来显示消除执行后发生的事件的消息。

您将使用常量函数和公共变量执行非常类似的操作。因为它们不需要运行气体(除了大的常量函数),你可以像这样执行它们:

yourContractInstance.maxAmountOfBets((err,result)=> { 
   if(result!= null)//做某事
})

这就是你如何得到公共变量maxAmountOfBets 。

现在我用我的风格开发了前端的代码。这是完整的代码:

import来自'react'的React 
导入来自'react-dom' 
的ReactDOM 
导入来自'web3'import './../css/index.css'的Web3
class App扩展了React.Component { 
   constructor(props){ 
      super(props)
      this.state = { 
         lastWinner:0,
         numberOfBets:0,
         minimumBet:0,
         totalBet:0,
         maxAmountOfBets:0,
      }
if(typeof web3!='undefined'){ 
         console.log(“使用从Metamask等外部源检测到的web3”)
         this.web3 = new Web3(web3.currentProvider)
      } else { 
         console.log(“未检测到web3。下降回到http:// localhost:8545。你应该在部署live时删除这个回退,因为它本身就是不安全的。考虑切换到Metamask进行开发。更多信息请访问: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”:[],“paid”:false,“type”:“函数 “},{” 恒定 “:真” 输入 “:[],” 姓名 “:” numberOfBets “ ”输出“:[{ ”名称“: ”“, ”类型“: ”uint256“}],”应付 “:假,” 类型 “:” 功能 “},{” 恒定 “:假” 输入 “:[{” 名称 “:” 播放 “ ”类型“: ”地址“}], ”名称“:” checkPlayerExists “ ”输出“:[{ ”名称“: ”“, ”类型“: ”布尔“}], ”应付“:假, ”类型“: ”功能“},{ ”恒定“:假” 输入端“:[],”命名 “:” 杀死”, “输出”:[], “应付”:假, “类型”: “功能”},{ “恒定”:假 “输入”:[], “姓名”: “resetData” “输出”:[], “应付”:假, “类型”: “功能”},{ “恒定”:假 “输入”:[{ “名称”: “maxBets”, “类型”:“uint256 “}],” 姓名 “:” setMaxAmountOfBets”, “输出”:[], “应付”:假, “类型”: “功能”},{ “恒定”:假 “输入”:[{ “姓名” “号码”, “类型”: “uint256”}], “名称”: “下注”, “输出”:[], “应付”:真, “类型”: “功能”},{ “恒定”:假, “输入”:[],”命名 “:” distributePrizes”, “输出”:[], “应付”:假, “类型”: “功能”},{ “恒定”:真 “输入”:[], “姓名”: “numberWinner” “输出”:[{ “名称”: “”, “类型”: “uint256”}], “应付”:假, “类型”: “功能”},{ “恒定”:真 “输入”: [], “名称”: “minimumBet”, “输出”:[{ “名称”: “”, “类型”: “uint256”}], “应付”:假, “类型”: “功能”},{ “恒定”:真 “输入”:[], “姓名”: “maxAmountOfBets”, “输出”:[{ “名称”: “”, “类型”: “uint256”}], “应付”:假, “类型”:”函数 “},{” 恒定 “:真” 输入 “:[{” 名称 “:” “ ”类型“: ”uint256“}], ”名称“: ”玩家“, ”输出“:[{” 名“:” “ ”类型“: ”地址“}], ”应付“:假, ”类型“: ”功能“},{ ”恒定“:真 ”输入“:[], ”姓名“:” totalBet ”, “输出”:[{ “名称”: “”, “类型”: “uint256”}], “应付”:假, “类型”: “功能”},{ “输入”:[{ “姓名” : “_ minimumBet”, “类型”: “uint256”},{ “名称”: “_ maxAmountOfBets”, “类型”: “uint256”}], “应付”:假, “类型”: “构造”},{”应付“:真”类型 “:” 回退“}])
      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,结果)=> { 
         如果(结果= NULL){!
            this.setState({ 
               maxAmountOfBets:parseInt函数(结果)
            })
         } 
      })
   }
//侦听事件并执行voteNumber方法
   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 => {
//删除
               为其选择的其他数字(let i = 0; i <liNodes.length; i ++){ 
                  liNodes [i] .className ='' 
               } 
            })
         })
      })
   }
voteNumber(number,cb){ 
      let bet = this.refs ['ether-bet']。value
如果(!下注)下注= 0.1
if(parseFloat(bet)<this.state.minimumBet){ 
         alert('你必须下注超过最小值')
         cb()
      } else { 
         this.state.ContractInstance.bet(number,{ 
            gas:300000,
            from:web3 .eth.accounts [0],
            value:web3.toWei(bet,'ether')
         },(错误,结果)=> { 
            cb()
         })
      } 
   }
render(){ 
      return(
         <div className =“main-container”> 
            <h1>打赌你最好的数字并赢得大量的以太</ h1>
<div className =“block”> 
               <b>投注数量:</ b>&nbsp; 
               <span> {this.state.numberOfBets} </ span> 
            </ div>
<div className =“block”> 
               <b>最后号码获胜者:</ b>&nbsp; 
               <span> {this.state.lastWinner} </ span> 
            </ div>
<div className =“block”> 
               <b>总以太赌注:</ b>&nbsp; 
               <span> {this.state.totalBet} ether </ span> 
            </ div>
<div className =“block”> 
               <b>最低赌注:</ b>&nbsp; 
               <span> {this.state.minimumBet} ether </ span> 
            </ div>
<div className =“block”> 
               <b>最大投注金额:</ b>&nbsp; 
               <span> {this.state.maxAmountOfBets} ether </ span> 
            </ div>
<HR />
<h2>投票给下一个号码</ h2>
<label> 
               <b>您想要下注多少以太?<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 
   ')

我在开始时更新应用程序的状态,当组件被挂载时,设置重要的变量,如总以太赌注。

然后我为每个<li>数字设置了单击事件监听器,因此当您单击它时,它会bet()使用正确的数字和您想要下注的以太量执行合同功能。

现在看起来像这样:

最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp

完整的代码可以在我的github中找到:https//github.com/merlox/casino-ethereum

恭喜你,如果你做到这一点!很少有人真正阅读完整的内容,你会成为其中之一吗?


4.使用IPFS在线部署应用程序

现在应用程序已经完成,是时候将它移动到在线世界,永久免费托管和分散,这有多酷?

这就是IPFS的力量。

嗯,实际上它并不是那么好,因为只要节点保持在线,你的应用就会在线。这就像洪流p2p电影,你可以下载它,只要有人播种它。

但它将完全分散,所以值得这样做。

让我们首先安装IPFS,访问他们的网站并安装应用程序:https//ipfs.io/docs/install/

在Windows中,我必须将文件夹添加到path变量中,以便能够ipfs从任何地方执行命令。如果您正在阅读此内容,则必须知道如何修改路径变量。

安装后,转到桌面并执行以下命令:

ipfs守护进程

这将创建一个节点。在另一个命令行或终端中:

ipfs swarm同行

这将为您提供分享您内容的同行。

ipfs add -r dist /

这会将您的dist文件夹添加到网络中。您将看到为您生成的长哈希值。最后一个哈希是该文件夹的唯一标识符:

添加了Qmc9HzLPur2ncuUArLjAaa4t2HrXFycgjUPb6122N6tzi2 dist / build.js 
添加了QmZBaGYWsACJ5aCFhW459xHZ8hk4YazX1EQFiSenu3ANfR dist / index.html 
添加了QmfZoCnPcgmHYmFJqHBcyuFh3FEYrTZqGdGyioSMrAZzw2 dist

复制最后一个哈希并执行:

ipfs名称发布 QmfZoCnPcgmHYmFJqHBcyuFh3FEYrTZqGdGyioSMrAZzw2

你会得到这样的东西:

发布到QmRDVed784YwKrYAgiiBbh2rFGfCUemXWk3NkD7nWdstER:/ ipfs / QmfZoCnPcgmHYmFJqHBcyuFh3FEYrTZqGdGyioSMrAZzw2

这意味着您的内容在该网址中可用,即第一个。您可以转到gateway.ipfs.io/ipns/ <your-hash-here>进行检查

在我的情况是:

gateway.ipfs.io/ipns/QmRDVed784YwKrYAgiiBbh2rFGfCUemXWk3NkD7nWdstER

加载需要一些时间,因为此时网络还不是很大。然后你会看到那个惊人的Dapp!记得在上面设置Metamask Ropsten Test Network 。

如果您更改您的文件,记得要执行webpack,然后ipfs add -r dist/ipfs name publish <the-hash> 。您将意识到发布名称哈希始终是相同的。

你的Dapp在线。


5.为应用程序使用自定义域

现在您可能想要使用自己的域名,因为对您的项目使用这样的网址有点不公平。

如果您需要完整的详细信息,IPFS的这个优秀的教程示例将清楚地向您解释。

我会解释你如何与godaddy一起做。它与其他提供商几乎相同。

  1. 首先转到godaddy域中的DNS管理器。
  2. 使用Host:@和Value添加新的TXT记录: dnslink=/ipns/<your-hash>
  3. 保存并更改或添加A记录与主持人:@指向:217.182.195.23 。那是gateway.ipfs.io的原始IP。
  4. 删除任何其他A记录,因为他们可能会对此新A记录进行干预。
  5. 保存它,几分钟后进入该域并检查它是否正常工作。
  6. 您也可以访问:gateway.ipfs.io/ipns/ <yourdomain>来访问它
  7. 在这种情况下,网址是gateway.ipfs.io/peppo.espeppo.es
最终的端到端教程,用于在以太坊中创建和部署完全分散的Dapp
看,它有效!我的域名是peppo.es

6.与最后的Dapp一起玩

这是整个教程。如果你读到它结束,恭喜!你很棒,现在能够创建不可阻挡的应用程序,如ethereum.org页面所声称的那样。

享受您的efford并确保尽可能多地测试应用程序以查找任何错误。如果您足够勇敢,可以将其部署到Homestead主网络。

唯一的区别是您在部署时必须更改Metamask网络。

此外,我建议你检查密实度文档时,Web3.js文档中,松露文档IPFS文档。他们拥有了解更多信息并创建大量应用程序所需的一切。

您可以学习使用松露并更快地开发应用程序,因为您可以从命令行部署它们,尽管您需要为此下载整个区块链。

您还可以使用Oraclize合约生成真正随机的数字。如果您有兴趣将Dapp扩展到专业人员,请搜索它们。

最后让我知道是否有不清楚的事情,可能的错误和有关该项目的问题。

你做了吗?

原创文章,作者:Eric,如若转载,请注明出处:https://ipfsdrop.com/tech/the-final-end-to-end-tutorial-is-used-to-create-and-deploy-in-the-ethernet-workshop/

发表评论

电子邮件地址不会被公开。 必填项已用*标注

联系我们

(+86)18301922335

在线咨询:点击这里给我发消息

邮件:haskell@freechains.cn

工作时间:7×24小时

QR code