Skip to main content

Vote

秘密

简单的投票合约示例

以下是一个简单的投票合约示例,它允许用户创建提案,投票,并根据投票结果决定提案是否通过。这个合约使用了 Solidity 编写,并包含基本的功能和安全检查。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleVoting {
// 提案的结构体
struct Proposal {
string description; // 提案的描述
uint voteCount; // 投票计数
}

address public chairperson; // 主席地址(合约的拥有者)

mapping(address => bool) public voters; // 记录投票人的状态
Proposal[] public proposals; // 提案数组

// 事件
event ProposalCreated(uint proposalId, string description);
event Voted(uint proposalId, address voter);
event VotingRightsGranted(address voter);

// 构造函数,初始化主席地址
constructor() {
chairperson = msg.sender;
}

// 授权投票权
function grantVotingRights(address voter) public {
require(msg.sender == chairperson, "Only chairperson can grant voting rights.");
require(!voters[voter], "The voter already has voting rights.");
voters[voter] = true;
emit VotingRightsGranted(voter);
}

// 创建提案
function createProposal(string memory description) public {
require(voters[msg.sender], "Only voters can create proposals.");
proposals.push(Proposal({
description: description,
voteCount: 0
}));
emit ProposalCreated(proposals.length - 1, description);
}

// 投票
function vote(uint proposalId) public {
require(voters[msg.sender], "Only voters can vote.");
require(proposalId < proposals.length, "Invalid proposal id.");

Proposal storage proposal = proposals[proposalId];
proposal.voteCount += 1;
emit Voted(proposalId, msg.sender);
}

// 获取提案的数量
function getProposalsCount() public view returns (uint) {
return proposals.length;
}

// 获取特定提案的详情
function getProposal(uint proposalId) public view returns (string memory description, uint voteCount) {
require(proposalId < proposals.length, "Invalid proposal id.");
Proposal storage proposal = proposals[proposalId];
return (proposal.description, proposal.voteCount);
}
}

这个合约提供了以下功能:

  • 主席(合约的部署者)可以授权给其他用户投票权。
  • 被授权的用户可以创建提案。
  • 被授权的用户可以对提案进行投票。
  • 任何人可以查看提案的数量和具体提案的详情。

这个合约是非常基础的投票合约,没有实现一些高级特性,例如提案的截止时间、投票权重、提案的执行等。在实际应用中,你可能需要根据具体需求来扩展这个合约的功能。

竞猜投票

一个竞猜投票合约通常允许用户对某个事件的结果进行投票,并在事件结束后根据结果分配奖励。以下是一个简单的竞猜投票合约示例,它允许用户对两个选项进行投票,并在竞猜结束后由合约的拥有者确定胜出的选项,然后分配奖金给猜中的用户。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GuessingGame {
// 竞猜的选项
enum Option { A, B }

// 竞猜的状态
enum State { Open, Closed, Decided }

// 竞猜的信息
struct Guess {
address guesser; // 竞猜者地址
Option option; // 竞猜的选项
uint amount; // 竞猜的金额
}

address public owner; // 合约的拥有者
State public state; // 竞猜的当前状态
Option public winningOption; // 胜出的选项
Guess[] public guesses; // 所有的竞猜

// 存储每个选项的总金额
mapping(Option => uint) public totalAmounts;

// 事件
event Guessed(address guesser, Option option, uint amount);
event WinnerDecided(Option winningOption);

// 构造函数
constructor() {
owner = msg.sender;
state = State.Open;
}

// 竞猜函数
function guess(Option option) external payable {
require(state == State.Open, "Guessing is closed.");
require(msg.value > 0, "You must send some ether to guess.");

guesses.push(Guess({
guesser: msg.sender,
option: option,
amount: msg.value
}));

totalAmounts[option] += msg.value;
emit Guessed(msg.sender, option, msg.value);
}

// 由拥有者关闭竞猜
function closeGuessing() external {
require(msg.sender == owner, "Only owner can close guessing.");
require(state == State.Open, "Guessing is not open.");
state = State.Closed;
}

// 由拥有者决定胜出的选项
function decideWinner(Option option) external {
require(msg.sender == owner, "Only owner can decide winner.");
require(state == State.Closed, "Guessing must be closed first.");

winningOption = option;
state = State.Decided;
emit WinnerDecided(winningOption);
}

// 用户领取奖金
function claimReward() external {
require(state == State.Decided, "Winner has not been decided yet.");

uint reward = 0;
for (uint i = 0; i < guesses.length; i++) {
if (guesses[i].guesser == msg.sender && guesses[i].option == winningOption) {
// 计算奖金
uint guesserTotal = totalAmounts[guesses[i].option];
uint poolTotal = totalAmounts[Option.A] + totalAmounts[Option.B];
reward += guesses[i].amount + (poolTotal - guesserTotal) * guesses[i].amount / guesserTotal;
}
}

require(reward > 0, "You have no reward to claim.");
payable(msg.sender).transfer(reward);
}
}

这个合约提供了以下功能:

  • 用户可以在竞猜开放时,对选项 A 或 B 进行投注。
  • 合约的拥有者可以在竞猜结束后关闭竞猜,并决定胜出的选项。
  • 用户可以在胜出选项被确定后,根据自己的竞猜情况领取奖金。

请注意,这个合约是一个非常简化的示例,它没有考虑到所有可能的安全性问题,如重入攻击、整数溢出等。在实际部署之前,需要进行详尽的测试和安全审计。此外,合约中的奖金分配逻辑假设每个猜中的用户按照其投注的比例分配剩余的奖金池,这可能需要进一步优化以适应不同的游戏规则。

比如竞猜时候质押一定的 usdt,结束后,获胜者按投注比例,提取 usdt 奖励

以下是包含 USDT 质押和奖励分配的秘密竞猜游戏的完整智能合约代码。请注意,这只是一个示例,实际部署之前需要进行详细的测试和审计。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SecretGuessingGame is ReentrancyGuard {
enum Option { A, B, C }
enum State { Betting, Reveal, Ended }

struct Bet {
bytes32 hash;
uint256 amount;
}

State public state;
address public owner;
mapping(address => Bet) public bets;
mapping(Option => uint256) public votes;
mapping(Option => address[]) public betters;
mapping(address => uint256) public rewards;
Option public winningOption;
uint256 public revealDeadline;
uint256 public totalBets;
IERC20 public usdtToken;

event BetPlaced(address indexed guesser, uint256 amount);
event Reveal(address indexed guesser, Option option);
event WinningOption(Option option);

modifier inState(State _state) {
require(state == _state, "Invalid state for this action.");
_;
}

constructor(address _usdtTokenAddress) {
owner = msg.sender;
state = State.Betting;
usdtToken = IERC20(_usdtTokenAddress);
}

function placeBet(bytes32 hash, uint256 amount) external inState(State.Betting) {
require(amount > 0, "Must send USDT to place a bet.");
require(bets[msg.sender].amount == 0, "Already placed a bet.");

require(usdtToken.transferFrom(msg.sender, address(this), amount), "USDT transfer failed.");

bets[msg.sender] = Bet({
hash: hash,
amount: amount
});
totalBets += amount;

emit BetPlaced(msg.sender, amount);
}

function closeBetting() external inState(State.Betting) {
require(msg.sender == owner, "Only the owner can close the betting phase.");
state = State.Reveal;
revealDeadline = block.timestamp + 24 hours;
}

function revealVote(Option option, string memory secret) external inState(State.Reveal) {
require(block.timestamp <= revealDeadline, "Reveal phase has ended.");
Bet storage bet = bets[msg.sender];
require(bet.amount > 0, "No bet placed.");
require(keccak256(abi.encodePacked(option, secret)) == bet.hash, "Invalid option or secret.");

votes[option] += bet.amount;
betters[option].push(msg.sender);

emit Reveal(msg.sender, option);
delete bets[msg.sender];
}

function determineWinner() external inState(State.Reveal) {
require(block.timestamp > revealDeadline, "Reveal phase has not ended yet.");

uint256 maxVotes = votes[Option.A];
winningOption = Option.A;

if (votes[Option.B] > maxVotes) {
maxVotes = votes[Option.B];
winningOption = Option.B;
}

if (votes[Option.C] > maxVotes) {
maxVotes = votes[Option.C];
winningOption = Option.C;
}

uint256 winningTotalBets = votes[winningOption];
for (uint256 i = 0; i < betters[winningOption].length; i++) {
address better = betters[winningOption][i];
uint256 betterBet = bets[better].amount;
rewards[better] = totalBets * betterBet / winningTotalBets;
}

state = State.Ended;
emit WinningOption(winningOption);
}

function claimReward() external nonReentrant inState(State.Ended) {
uint256 reward = rewards[msg.sender];
require(reward > 0, "No reward to claim.");

rewards[msg.sender] = 0;
require(usdtToken.transfer(msg.sender, reward), "USDT transfer failed.");
}

function generateBetHash(Option option, string memory secret) public pure returns (bytes32) {
return keccak256(abi.encodePacked(option, secret));
}
}

在这个合约中,totalBets变量记录了所有下注的总额,rewards映射记录了每个用户的奖励金额。在determineWinner函数中,我们计算出获胜选项的总投注额,并按照每个赌注者的投注比例计算其奖励。

claimReward函数中,用户可以领取他们的奖励,该函数会调用 USDT 代币合约的transfer函数来向用户发送代币。

请记住,智能合约处理金融交易,因此它们的安全性至关重要。在部署之前,应该由专业的审计人员进行彻底的审计,以确保合约没有漏洞或其他安全问题。此外,由于 USDT 合约可能有不同的实现(例如 Tether 的 USDT 和其他基于 ERC20 的 USDT 实现),请确保你使用的合约地址是正确的,并且该合约符合标准的 ERC20 接口。

揭秘时候也需要 gas 吧

是的,以太坊网络上的任何交易,包括揭示秘密(revealVote 函数)这样的操作,都需要支付 gas 费。Gas 是以太坊网络上执行操作所需的计算资源的度量单位。每个操作都有一个基础的 gas 成本,这取决于它的复杂性和所需的计算资源。

当用户调用revealVote函数时,他们必须支付足够的 gas 费用来完成这个操作。gas 费用由两部分组成:

  1. Gas Limit:这是用户愿意为执行这笔交易支付的最大 gas 数量。如果交易消耗的 gas 超过了设定的 gas limit,交易将会失败,已消耗的 gas 不会退还。

  2. Gas Price:这是用户愿意为每单位 gas 支付的价格,通常以 gwei(1 gwei = 10^-9 ETH)为单位。网络拥堵时,用户可能会提高 gas 价格以确保他们的交易能够快速被矿工打包进区块。

总的 gas 费用是通过将消耗的 gas 数量乘以 gas 价格来计算的。在以太坊网络上,矿工优先打包 gas 价格较高的交易。

智能合约开发者应该努力优化他们的合约代码,以减少交易的 gas 消耗,从而为用户节省费用。用户在执行交易之前,应该使用工具如以太坊的 Gas Estimator 来估算所需的 gas 费用,以确保他们设置了合适的 gas limit 和 gas price。