本篇教程為在以太坊區(qū)塊鏈上發(fā)布一個(gè)智能彩票合約,活動(dòng)期間用戶可充值一定金額的以太幣到合約地址,活動(dòng)結(jié)束進(jìn)行開獎(jiǎng),隨機(jī)從參與用戶中抽取3人,平分獎(jiǎng)池獎(jiǎng)金。(分為上下兩篇)(本篇為下)
教程思路:
- 新建以太坊錢包
- 充值一定數(shù)額ETH作為合約發(fā)布汽油費(fèi)
- Remix編寫solidity智能合約
- 發(fā)布到鏈上
再次明確合約發(fā)布的流程:
用solidity或Vyper編寫好智能合約代碼=>編譯為EVM可執(zhí)行的字節(jié)碼=>打包整個(gè)交易廣播給以太坊的某些節(jié)點(diǎn)=>等待其被放入某個(gè)區(qū)塊=>完成
編寫Solidity智能合約:
Remix是什么:
Remix是一個(gè)專門編寫智能合約的 WebIDE(web集成環(huán)境),支持Solidity和Vyper,可從瀏覽器直接訪問,并且連接到 MetaMask從而發(fā)布交易。
所以我們只需要編寫智能合約的代碼,Remix會(huì)自動(dòng)幫我們編譯為EVM字節(jié)碼,發(fā)布到區(qū)塊鏈中。
跳轉(zhuǎn)Remix
在自動(dòng)幫我們創(chuàng)建好的contracts目錄下新建Lottery.sol:
代碼思路:
構(gòu)造函數(shù):
- 設(shè)置合約擁有者為部署合約的用戶。
- 傳入彩票的活動(dòng)的持續(xù)時(shí)間,
- 將彩票活動(dòng)標(biāo)記為False(未結(jié)束)
用戶參與函數(shù):
- 允許任何用戶調(diào)用
- 要求至少0.1個(gè)ETH才能參與活動(dòng)
- 檢查活動(dòng)是否結(jié)束
- 活動(dòng)未結(jié)束=>將參與用戶的地址添加到參與者組中
- 觸發(fā)彩票參與事件=>記錄日志
彩票結(jié)束函數(shù):
- 合約擁有者可調(diào)用
- 檢查是否已到結(jié)束時(shí)間
- 檢查彩票活動(dòng)是否已標(biāo)記為False(未結(jié)束)
- 檢查是否有足夠的參與者
- 將彩票活動(dòng)標(biāo)記為True(已結(jié)束)
- 開獎(jiǎng),從參與者數(shù)組中挑選三人
- 獎(jiǎng)金轉(zhuǎn)賬給中獎(jiǎng)?wù)?/li>
- 觸發(fā)彩票結(jié)束事件=>記錄日志
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 定義一個(gè)彩票合約
contract Lottery {
address public owner; // 合約的擁有者
address payable[] public participants; // 參與彩票的用戶數(shù)組
uint256 public lotteryEndTime; // 彩票活動(dòng)的結(jié)束時(shí)間
bool public lotteryEnded; // 彩票活動(dòng)是否已結(jié)束的標(biāo)志
// 定義一個(gè)事件,當(dāng)用戶參與彩票時(shí)觸發(fā)
event LotteryEnter(address indexed participant);
// 定義一個(gè)事件,當(dāng)彩票結(jié)束時(shí)觸發(fā),記錄中獎(jiǎng)?wù)叩刂? event LotteryEnd(address winner1, address winner2, address winner3);
// 構(gòu)造函數(shù),設(shè)置彩票活動(dòng)的持續(xù)時(shí)間
constructor(uint _durationMinutes) {
owner = msg.sender; // 設(shè)置合約擁有者為部署合約的用戶
lotteryEndTime = block.timestamp + (_durationMinutes * 1 minutes); // 計(jì)算彩票活動(dòng)的結(jié)束時(shí)間
lotteryEnded = false; // 初始化彩票活動(dòng)未結(jié)束的標(biāo)志
}
// 允許用戶參與彩票的函數(shù)
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"); // 檢查彩票活動(dòng)是否已結(jié)束
require(!lotteryEnded, "Lottery already ended"); // 檢查彩票活動(dòng)是否已標(biāo)記為結(jié)束
participants.push(payable(msg.sender)); // 將用戶添加到參與者數(shù)組
emit LotteryEnter(msg.sender); // 觸發(fā)參與彩票的事件
}
// 結(jié)束彩票活動(dòng)的函數(shù),只能由合約擁有者調(diào)用
function endLottery() public {
require(msg.sender == owner, "Only owner can end the lottery"); // 只有合約擁有者可以結(jié)束彩票活動(dòng)
require(block.timestamp >= lotteryEndTime, "Lottery not yet ended"); // 檢查彩票活動(dòng)是否已到結(jié)束時(shí)間
require(!lotteryEnded, "Lottery already ended"); // 檢查彩票活動(dòng)是否已標(biāo)記為結(jié)束
require(participants.length >= 3, "Not enough participants"); // 檢查是否有足夠的參與者
lotteryEnded = true; // 標(biāo)記彩票活動(dòng)為已結(jié)束
uint256 prize = address(this).balance / 3; // 計(jì)算每個(gè)中獎(jiǎng)?wù)叩莫?jiǎng)金
// 為了避免重復(fù)中獎(jiǎng),我們需要一個(gè)方法來確保每個(gè)中獎(jiǎng)?wù)呤俏ㄒ坏? // 以下是一個(gè)簡單的解決方案,但請注意,這并不是一個(gè)安全的隨機(jī)數(shù)生成方法
// 在生產(chǎn)環(huán)境中,應(yīng)該使用更安全的隨機(jī)數(shù)生成方法,如Chainlink VRF
address[3] memory winnersAddresses; // 創(chuàng)建一個(gè)固定大小為3的地址數(shù)組來存儲(chǔ)中獎(jiǎng)?wù)叩刂? uint256[] memory winners = new uint256[](3); // 創(chuàng)建一個(gè)大小為3的數(shù)組來存儲(chǔ)中獎(jiǎng)?wù)叩乃饕? for (uint i = 0; i < 3; i++) {
uint256 winnerIndex;
bool isUnique;
do {
winnerIndex = random() % participants.length; // 隨機(jī)選擇一個(gè)參與者作為中獎(jiǎng)?wù)? isUnique = true; // 假設(shè)選中的參與者是唯一的
for (uint j = 0; j < i; j++) {
if (winners[j] == winnerIndex) {
isUnique = false; // 如果已經(jīng)選中過,標(biāo)記為不唯一
break;
}
}
} while (!isUnique); // 如果不唯一,重新選擇
winners[i] = winnerIndex; // 記錄中獎(jiǎng)?wù)叩乃饕? winnersAddresses[i] = participants[winnerIndex]; // 記錄中獎(jiǎng)?wù)叩牡刂? participants[winnerIndex].transfer(prize); // 將獎(jiǎng)金轉(zhuǎn)賬給中獎(jiǎng)?wù)? }
// 循環(huán)結(jié)束,觸發(fā)彩票結(jié)束的事件
emit LotteryEnd(winnersAddresses[0], winnersAddresses[1], winnersAddresses[2]);
}
// 生成隨機(jī)數(shù)的私有函數(shù),用于選擇中獎(jiǎng)?wù)? function random() private view returns (uint) {
// 使用區(qū)塊難度、時(shí)間戳和參與者數(shù)組作為種子生成隨機(jī)數(shù)
return uint(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, participants)));
}
}
在Solidity中,事件(Event)是智能合約的一種機(jī)制,用于在區(qū)塊鏈上記錄日志信息。當(dāng)特定的函數(shù)被執(zhí)行時(shí),可以觸發(fā)這些事件,并將信息記錄到區(qū)塊鏈上。這些記錄是不可變的,且可以被區(qū)塊鏈上的任何人查詢,但不會(huì)影響區(qū)塊鏈的狀態(tài)。
上述代碼中隨機(jī)數(shù)生成方法不夠安全,在實(shí)際應(yīng)用中,應(yīng)該使用專門的隨機(jī)數(shù)服務(wù),如Chainlink VRF,以確保公平性和安全性。
合約如何發(fā)布:
編譯:
點(diǎn)擊代碼左上綠色三角,編譯通過:
連接到MetaMask:
默認(rèn)發(fā)布環(huán)境為Remix為我們提供的虛擬機(jī),可在虛擬環(huán)境測試無誤后再正式發(fā)布。
我們點(diǎn)擊切換到Inject Provider-MetaMask環(huán)境,錢包自動(dòng)彈出:
連接成功后,左側(cè)可以看到我們的賬戶地址、余額,汽油費(fèi)上限。在Deploy右側(cè)可以看到我們在構(gòu)造函數(shù)中設(shè)置的活動(dòng)持續(xù)時(shí)間,設(shè)置為60min,點(diǎn)擊Deploy發(fā)布。
部署成功后,可以在已部署合同中看到一條記錄,點(diǎn)開可以看到我們的智能合約,函數(shù)顯示在左側(cè)供調(diào)用。文章來源:http://www.zghlxwxcb.cn/news/detail-852437.html
也可以打開Etherscan進(jìn)行更詳細(xì)的查看:文章來源地址http://www.zghlxwxcb.cn/news/detail-852437.html
到了這里,關(guān)于web3系列———4.編寫第一個(gè)智能合約并發(fā)布到鏈上的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!