赞
踩
本篇教程为在以太坊区块链上发布一个智能彩票合约,活动期间用户可充值一定金额的以太币到合约地址,活动结束进行开奖,随机从参与用户中抽取3人,平分奖池奖金。(分为上下两篇)(本篇为下)
教程思路:
用solidity或Vyper编写好智能合约代码=>编译为EVM可执行的字节码=>打包整个交易广播给以太坊的某些节点=>等待其被放入某个区块=>完成
Remix是一个专门编写智能合约的 WebIDE(web集成环境),支持Solidity和Vyper,可从浏览器直接访问,并且连接到 MetaMask从而发布交易。
所以我们只需要编写智能合约的代码,Remix会自动帮我们编译为EVM字节码,发布到区块链中。
跳转Remix
在自动帮我们创建好的contracts目录下新建Lottery.sol:
构造函数:
用户参与函数:
彩票结束函数:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // 定义一个彩票合约 contract Lottery { address public owner; // 合约的拥有者 address payable[] public participants; // 参与彩票的用户数组 uint256 public lotteryEndTime; // 彩票活动的结束时间 bool public lotteryEnded; // 彩票活动是否已结束的标志 // 定义一个事件,当用户参与彩票时触发 event LotteryEnter(address indexed participant); // 定义一个事件,当彩票结束时触发,记录中奖者地址 event LotteryEnd(address winner1, address winner2, address winner3); // 构造函数,设置彩票活动的持续时间 constructor(uint _durationMinutes) { owner = msg.sender; // 设置合约拥有者为部署合约的用户 lotteryEndTime = block.timestamp + (_durationMinutes * 1 minutes); // 计算彩票活动的结束时间 lotteryEnded = false; // 初始化彩票活动未结束的标志 } // 允许用户参与彩票的函数 function enterLottery() public payable { require(msg.value == 0.1 ether, "Must send exactly 0.1 ETH"); // 要求用户支付0.1以太币 require(block.timestamp <= lotteryEndTime, "Lottery has ended"); // 检查彩票活动是否已结束 require(!lotteryEnded, "Lottery already ended"); // 检查彩票活动是否已标记为结束 participants.push(payable(msg.sender)); // 将用户添加到参与者数组 emit LotteryEnter(msg.sender); // 触发参与彩票的事件 } // 结束彩票活动的函数,只能由合约拥有者调用 function endLottery() public { require(msg.sender == owner, "Only owner can end the lottery"); // 只有合约拥有者可以结束彩票活动 require(block.timestamp >= lotteryEndTime, "Lottery not yet ended"); // 检查彩票活动是否已到结束时间 require(!lotteryEnded, "Lottery already ended"); // 检查彩票活动是否已标记为结束 require(participants.length >= 3, "Not enough participants"); // 检查是否有足够的参与者 lotteryEnded = true; // 标记彩票活动为已结束 uint256 prize = address(this).balance / 3; // 计算每个中奖者的奖金 // 为了避免重复中奖,我们需要一个方法来确保每个中奖者是唯一的 // 以下是一个简单的解决方案,但请注意,这并不是一个安全的随机数生成方法 // 在生产环境中,应该使用更安全的随机数生成方法,如Chainlink VRF address[3] memory winnersAddresses; // 创建一个固定大小为3的地址数组来存储中奖者地址 uint256[] memory winners = new uint256[](3); // 创建一个大小为3的数组来存储中奖者的索引 for (uint i = 0; i < 3; i++) { uint256 winnerIndex; bool isUnique; do { winnerIndex = random() % participants.length; // 随机选择一个参与者作为中奖者 isUnique = true; // 假设选中的参与者是唯一的 for (uint j = 0; j < i; j++) { if (winners[j] == winnerIndex) { isUnique = false; // 如果已经选中过,标记为不唯一 break; } } } while (!isUnique); // 如果不唯一,重新选择 winners[i] = winnerIndex; // 记录中奖者的索引 winnersAddresses[i] = participants[winnerIndex]; // 记录中奖者的地址 participants[winnerIndex].transfer(prize); // 将奖金转账给中奖者 } // 循环结束,触发彩票结束的事件 emit LotteryEnd(winnersAddresses[0], winnersAddresses[1], winnersAddresses[2]); } // 生成随机数的私有函数,用于选择中奖者 function random() private view returns (uint) { // 使用区块难度、时间戳和参与者数组作为种子生成随机数 return uint(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, participants))); } }
在Solidity中,事件(Event)是智能合约的一种机制,用于在区块链上记录日志信息。当特定的函数被执行时,可以触发这些事件,并将信息记录到区块链上。这些记录是不可变的,且可以被区块链上的任何人查询,但不会影响区块链的状态。
上述代码中随机数生成方法不够安全,在实际应用中,应该使用专门的随机数服务,如Chainlink VRF,以确保公平性和安全性。
点击代码左上绿色三角,编译通过:
默认发布环境为Remix为我们提供的虚拟机,可在虚拟环境测试无误后再正式发布。
我们点击切换到Inject Provider-MetaMask环境,钱包自动弹出:
连接成功后,左侧可以看到我们的账户地址、余额,汽油费上限。在Deploy右侧可以看到我们在构造函数中设置的活动持续时间,设置为60min,点击Deploy发布。
部署成功后,可以在已部署合同中看到一条记录,点开可以看到我们的智能合约,函数显示在左侧供调用。
也可以打开Etherscan进行更详细的查看:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。