本次分享如何使用前端页面调用智能合约并且发布合约到测试链
编辑合约代码-彩票系统
首先拆分需求
- 创建彩票奖金池 彩票单价
- 创建彩票购买人列表
- 特定时间或人数达到一定数量开出中级号码
- 将奖金发送至中奖人地址
代码示列如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Lottery {
// 合约所有者
address public owner;
// 彩票单价
uint public ticketPrice;
// 最大票数
uint public maxTickets;
// 合约结束时间
uint public endTimestamp;
// 奖金池
uint public prizePool;
// 当前总票数
uint public totalTickets;
// 参与者列表
address[] public participants;
// 每个地址对应的票数
mapping(address => uint) public tickets;
// 购买彩票事件
event TicketPurchased(address indexed buyer, uint ticketCount);
// 开奖事件
event LotteryEnded(address winner, uint prize);
// 合约构造函数,初始化彩票单价、最大票数和持续时间
constructor(uint _ticketPrice, uint _maxTickets, uint _durationMinutes) {
owner = msg.sender;
ticketPrice = _ticketPrice;
maxTickets = _maxTickets;
endTimestamp = block.timestamp + (_durationMinutes * 1 minutes);
}
// 仅允许合约所有者调用的方法修饰符
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner can perform this action.");
_;
}
// 彩票开放状态修饰符
modifier lotteryOpen() {
require(block.timestamp < endTimestamp, "The lottery has ended.");
require(totalTickets < maxTickets, "All tickets have been sold.");
_;
}
// 购买彩票函数,允许用户购买一定数量的彩票
function buyTickets(uint ticketCount) public payable lotteryOpen {
// 确保支付金额足够购买彩票
require(msg.value >= ticketPrice * ticketCount, "账户余额不足.");
// 如果用户是第一次购买彩票,记录该用户
if (tickets[msg.sender] == 0) {
participants.push(msg.sender);
}
// 更新用户的彩票数和总票数
tickets[msg.sender] += ticketCount;
totalTickets += ticketCount;
prizePool += msg.value;
// 触发购买彩票事件
emit TicketPurchased(msg.sender, ticketCount);
}
// 开奖函数,仅允许合约所有者调用
function drawWinner() public onlyOwner {
// 确保当前时间已到或所有彩票已售出
require(block.timestamp >= endTimestamp || totalTickets >= maxTickets, "The lottery is still ongoing.");
//确保有购彩人
require(participants.length > 0, "无人购买彩票.");
// 生成一个随机索引选出中奖者
uint randomIndex = getRandomNumber() % participants.length;
address winner = participants[randomIndex];
// 将奖金池金额重置并发送给中奖者
uint prize = prizePool;
prizePool = 0;
// 发送奖金
(bool success, ) = winner.call{value: prize}("");
require(success, "交易失败.");
// 触发开奖事件
emit LotteryEnded(winner, prize);
}
// 生成随机数函数
function getRandomNumber() internal view returns (uint) {
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, participants)));
}
// 获取参与者列表
function getParticipants() public view returns (address[] memory) {
return participants;
}
// 获取某个参与者的票数
function getTicketCount(address participant) public view returns (uint) {
return tickets[participant];
}
// 接收以太币
receive() external payable {}
}
合约调用之前我们需要创建本地区块链环境进行调试开发
ganache 进入此页面下载安装运行此程序
打开程序之后可以看到此界面
部署合约
前面已讲过remix的一些基本配置 所里这里不再赘述
- 将代码复制到remix
- 编译合约
- 选择部署网络 Dev - Ganache Provider 填写本地Ganache端口(Ganache 客户端页面有写)
- 选择账号(记住这个账号 开奖只能合约部署者操作) 很重要
- 在DEPLOY属性下填写初始化合约参数(彩票单价、最大票数和持续时间)(1 10 10) 点击transact
- 点击部署 复制控制台合约地址(contract address)
准备工作
- 首先确认浏览器和合约在同一区块链
- 进入google应用商店,下载MeteMask 插件
- 进入MeteMask设置->添加网络->手动添加网络
- 网络名称 -> Ganache
- RPC URL -> http://127.0.0.1:7545 (此地址在ganache客户端首页有展示)
- 链 ID -> 1337
- 保存网络->在metamask首页左上角切换网络至Ganache
- 点击账号下拉选项
- Add account
- 导入账户
- 在Ganache客户端界面打开accounts
- 点击账后右侧小钥匙图标复制 PRIVATE KEY 粘贴至钱包
- 确认导入
调用合约
注意 在购买彩票中切换付款账户 开奖账户即是合约部署账户
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/web3@1.7.0/dist/web3.min.js"></script>
</head>
<body>
<div>
<h2>购买彩票</h2>
<label for="ticketCount">购买彩票数量:</label>
<input type="number" id="ticketCount" value="1" />
<button onclick="buyTickets()">购买彩票</button>
</div>
<div>
<h2>开奖</h2>
<button onclick="drawWinner()">开奖</button>
</div>
<script>
// 合约地址和ABI
const contractAddress = "0xca59D3a64A50702C7DcC5530639d21A799911E3c";
//Application Binary Interface)是用于与以太坊智能合约进行交互的标准接口。
//它定义了合约中可调用的 函数及其参数和返回值,以及事件。
//ABI用于生成与合约交互的JavaScript代码,并用于对智能合约调用进行编码和解码。
//在 remix compiler页面最下方 编译好合约后可一键复制合约ABI后粘贴至此处
const contractABI = [];
let web3;
let lotteryContract;
let userAccount;
// 初始化Web3
window.addEventListener("load", async () => {
if (window.ethereum) {
web3 = new Web3(window.ethereum);
try {
// 请求用户账户访问权限
await window.ethereum.request({ method: "eth_requestAccounts" });
userAccount = (await web3.eth.getAccounts())[0];
lotteryContract = new web3.eth.Contract(
contractABI,
contractAddress
);
} catch (error) {
console.error("用户拒绝了账户访问");
}
} else if (window.web3) {
web3 = new Web3(window.web3.currentProvider);
userAccount = (await web3.eth.getAccounts())[0];
lotteryContract = new web3.eth.Contract(contractABI, contractAddress);
} else {
console.error("未检测到以太坊浏览器,建议安装MetaMask!");
}
});
// 购买彩票
async function buyTickets() {
const ticketCount = document.getElementById("ticketCount").value;
const ticketPrice = await lotteryContract.methods.ticketPrice().call();
const totalCost = ticketPrice * ticketCount;
try {
await lotteryContract.methods.buyTickets(ticketCount).send({
from: userAccount,
value: totalCost,
});
alert("购买彩票成功");
} catch (error) {
console.error("购买彩票失败", error);
alert("购买彩票失败");
}
}
// 开奖
async function drawWinner() {
try {
await lotteryContract.methods
.drawWinner()
.send({ from: userAccount });
alert("开奖成功");
} catch (error) {
console.error("开奖失败", error);
alert("开奖失败");
}
}
</script>
</body>
</html>
相信有一些想法的同学已经开心问了:我写java有spring boot 我写html有vue 那我写合约有没有框架呢 ?
下期预告
使用TRUFFLE编写部署调试合约(从前端到服务器再到合约,一个完整的链上拍卖实战项目)