为了增强该挑战的约束力和仪式感,我们要确保挑战规则和惩罚的落实,智能合约就是一个很好能够实现这一目标的技术。通过编写智能合约,我们可以在代码层面上确保规则的执行,保证规则的强制性和可信度。
这篇文章是该系列的第一篇,就让我们来尝试一下写一个这样的合约吧!我们将使用Solidity编写一个每周博客挑战智能合约,并部署到Polygon上。最近Sui刚上线,我个人也很喜欢Sui上的开发体验,后续可能还会做一个Sui版本的。
既然上面的需求描述得那么详细,那么我们可以先问问GPT,生成的代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract BlogChallenge {
// 挑战的数据结构
struct Challenge {
uint256 startTime; // 挑战开始时间
uint256 cycle; // 挑战周期
uint256 numberOfCycles; // 周期数
address challenger; // 挑战者地址
address[] participants; // 参与者地址
IERC20 penaltyToken; // 惩罚币种
uint256 penaltyAmount; // 惩罚金额
uint256 deposit; // 押金
bool[] blogSubmissions; // 博客提交情况
bool ended; // 挑战是否已结束
}
// 当前挑战
Challenge public currentChallenge;
// 押金倍数
uint256 public constant DEPOSIT_MULTIPLIER = 3;
// 设置挑战
function setChallenge(
uint256 _startTime,
uint256 _cycle,
uint256 _numberOfCycles,
address _challenger,
address[] memory _participants,
IERC20 _penaltyToken,
uint256 _penaltyAmount
) public {
// 初始化当前挑战
currentChallenge = Challenge({
startTime: _startTime,
cycle: _cycle,
numberOfCycles: _numberOfCycles,
challenger: _challenger,
participants: _participants,
penaltyToken: _penaltyToken,
penaltyAmount: _penaltyAmount,
deposit: _penaltyAmount * DEPOSIT_MULTIPLIER,
blogSubmissions: new bool[](_numberOfCycles),
ended: false
});
}
// 存入惩罚金
function depositPenalty() public {
// 只有挑战者可以存入
require(msg.sender == currentChallenge.challenger, "只有挑战者可以存入");
// 将押金从挑战者账户转到合约账户
require(currentChallenge.penaltyToken.transferFrom(msg.sender, address(this), currentChallenge.deposit), "转账失败");
}
// 提交博客
function submitBlog(bool _blogSubmitted) public {
// 只有挑战者可以提交
require(msg.sender == currentChallenge.challenger, "只有挑战者可以提交博客");
// 计算当前周期
uint256 currentCycle = (block.timestamp - currentChallenge.startTime) / currentChallenge.cycle;
// 记录博客提交情况
currentChallenge.blogSubmissions[currentCycle] = _blogSubmitted;
}
// 更新周期
function updateCycle() public {
// 计算当前周期
uint256 currentCycle = (block.timestamp - currentChallenge.startTime) / currentChallenge.cycle;
// 如果挑战者没有提交博客,则发放惩罚金
if (!currentChallenge.blogSubmissions[currentCycle]) {
if (currentChallenge.penaltyToken.balanceOf(currentChallenge.challenger) < currentChallenge.penaltyAmount) {
if (currentChallenge.deposit >= currentChallenge.penaltyAmount) {
currentChallenge.deposit -= currentChallenge.penaltyAmount;
} else {
endChallenge(false);
return;
}
} else {
currentChallenge.penaltyToken.transferFrom(currentChallenge.challenger, address(this), currentChallenge.penaltyAmount);
}
}
}
// 检查挑战是否成功
function checkSuccess() public view returns (bool) {
// 成功提交的次数
uint256 successfulSubmissions = 0;
// 遍历所有周期
for (uint256 i = 0; i < currentChallenge.numberOfCycles; i++) {
// 如果该周期提交了博客,成功次数加一
if (currentChallenge.blogSubmissions[i]) {
successfulSubmissions++;
}
}
// 如果成功次数达到总周期数的60%,则挑战成功
return successfulSubmissions >= currentChallenge.numberOfCycles * 60 / 100;
}
// 结束挑战
function endChallenge(bool _success) public {
// 挑战必须还未结束
require(!currentChallenge.ended, "挑战已经结束");
// 如果挑战成功,发放NFT(待实现)
if (_success) {
// TODO: 发放NFT
} else {
// 如果挑战失败,将剩余押金平均分给参与者
uint256 participantReward = currentChallenge.deposit / currentChallenge.participants.length;
for (uint256 i = 0; i < currentChallenge.participants.length; i++) {
currentChallenge.penaltyToken.transfer(currentChallenge.participants[i], participantReward);
}
}
// 标记挑战为已结束
currentChallenge.ended = true;
}
// 重置挑战
function resetChallenge() public {
// 当前挑战必须已结束
require(currentChallenge.ended, "当前挑战未结束");
// 删除当前挑战
delete currentChallenge;
}
// 提取押金
function withdrawDeposit() public {
// 只有挑战者可以提取押金
require(msg.sender == currentChallenge.challenger, "只有挑战者可以提取押金");
// 挑战必须已结束
require(currentChallenge.ended, "挑战未结束");
// 将剩余押金转给挑战者
currentChallenge.penaltyToken.transfer(msg.sender, currentChallenge.deposit);
}
}
好,本文完结。
我们先简单分析下AI给的代码,不难发现,至少有几点问题: