Safemath.sol 库被弃用后,如何在 Solidity 中乘以日期
How can I multiply dates in Solidity after Safemath.sol library is deprecated
我在 Solidity 0.6.0 中编译了这段代码,没有错误(我正在使用 Remix)。但是突然间 SafeMath 库被弃用了,因为不再需要导入它而且我不知道如何修复这一行:
uint raiseUntil = now.add(durationInDays.mul(1 天));
我使用 ¨mul¨ 函数计算未来日期。
我把下面的所有代码都给你。它是众筹平台的后端。 raisedUntil变量是众筹项目截止日期。
pragma solidity 0.6.0;
// Importing OpenZeppelin's SafeMath Implementation
import 'https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Crowdfunding {
using SafeMath for uint256;
// List of existing projects
Project[] private projects;
// Event that will be emitted whenever a new project is started
event ProjectStarted(
address contractAddress,
address projectStarter,
string projectTitle,
string projectDesc,
uint256 deadline,
uint256 goalAmount
);
/** @dev Function to start a new project.
* @param title Title of the project to be created
* @param description Brief description about the project
* @param durationInDays Project deadline in days
* @param amountToRaise Project goal in wei
*/
function startProject(
string calldata title,
string calldata description,
uint durationInDays,
uint amountToRaise
) external {
uint raiseUntil = now.add(durationInDays.mul(1 days));
Project newProject = new Project(msg.sender, title, description, raiseUntil, amountToRaise);
projects.push(newProject);
emit ProjectStarted(
address(newProject),
msg.sender,
title,
description,
raiseUntil,
amountToRaise
);
}
/** @dev Function to get all projects' contract addresses.
* @return A list of all projects' contract addreses
*/
function returnAllProjects() external view returns(Project[] memory){
return projects;
}
}
contract Project {
using SafeMath for uint256;
// Data structures
enum State {
Fundraising,
Expired,
Successful
}
// State variables
address payable public creator;
uint public amountGoal; // required to reach at least this much, else everyone gets refund
uint public completeAt;
uint256 public currentBalance;
uint public raiseBy;
string public title;
string public description;
State public state = State.Fundraising; // initialize on create
mapping (address => uint) public contributions;
// Event that will be emitted whenever funding will be received
event FundingReceived(address contributor, uint amount, uint currentTotal);
// Event that will be emitted whenever the project starter has received the funds
event CreatorPaid(address recipient);
// Modifier to check current state
modifier inState(State _state) {
require(state == _state);
_;
}
// Modifier to check if the function caller is the project creator
modifier isCreator() {
require(msg.sender == creator);
_;
}
constructor
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint fundRaisingDeadline,
uint goalAmount
) public {
creator = projectStarter;
title = projectTitle;
description = projectDesc;
amountGoal = goalAmount;
raiseBy = fundRaisingDeadline;
currentBalance = 0;
}
/** @dev Function to fund a certain project.
*/
function contribute() external inState(State.Fundraising) payable {
require(msg.sender != creator);
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
currentBalance = currentBalance.add(msg.value);
emit FundingReceived(msg.sender, msg.value, currentBalance);
checkIfFundingCompleteOrExpired();
}
/** @dev Function to change the project state depending on conditions.
*/
function checkIfFundingCompleteOrExpired() public {
if (currentBalance >= amountGoal) {
state = State.Successful;
payOut();
} else if (now > raiseBy) {
state = State.Expired;
}
completeAt = now;
}
/** @dev Function to give the received funds to project starter.
*/
function payOut() internal inState(State.Successful) returns (bool) {
uint256 totalRaised = currentBalance;
currentBalance = 0;
if (creator.send(totalRaised)) {
emit CreatorPaid(creator);
return true;
} else {
currentBalance = totalRaised;
state = State.Successful;
}
return false;
}
/** @dev Function to retrieve donated amount when a project expires.
*/
function getRefund() public inState(State.Expired) returns (bool) {
require(contributions[msg.sender] > 0);
uint amountToRefund = contributions[msg.sender];
contributions[msg.sender] = 0;
if (!msg.sender.send(amountToRefund)) {
contributions[msg.sender] = amountToRefund;
return false;
} else {
currentBalance = currentBalance.sub(amountToRefund);
}
return true;
}
/** @dev Function to get specific information about the project.
* @return Returns all the project's details
*/
function getDetails() public view returns
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint256 deadline,
State currentState,
uint256 currentAmount,
uint256 goalAmount
) {
projectStarter = creator;
projectTitle = title;
projectDesc = description;
deadline = raiseBy;
currentState = state;
currentAmount = currentBalance;
goalAmount = amountGoal;
}
}
您可能决定切换到较新版本的 Solidity(当前为 0.8.4),其中 now
是 deprecated,所以我已经使用 block.timestamp
。这是同一件事 - now
只是它的别名。
SafeMath 库检查两个无符号整数的算术运算(例如乘法或加法)是否 overflow/underflow。如果会,它会抛出异常。如果不是,则执行算术运算。由于 Solidity 0.8 这是自动完成的并且不需要 SafeMath,因此使用当前版本的另一个原因。
在 Solidity 0.8 和更新版本中,如果这会溢出,它会自动抛出异常
uint raiseUntil = block.timestamp + durationInDays * 1 days;
在旧版本中,您需要自己检查可能的溢出并从代码中抛出异常(与 SafeMath 的行为相同)。
uint durationInSeconds = durationInDays * 1 days;
// Check that the result of division (inverse operation to multiplication) is the original number.
// If it's not, throw an exception, because the multiplication overflowed.
require(durationInSeconds / durationInDays == 1 days, 'Multiplication overflow');
uint raiseUntil = block.timestamp + durationInSeconds;
// Check that the result of subtraction (inverse operation to addition) is the original number.
// If it's not, throw an exception, because the addition overflowed.
require(raiseUntil - block.timestamp == durationInSeconds, 'Addition overflow');
Integer overflow/underflow 是智能合约中最常见的安全漏洞之一。如果您的 startProject()
函数存在漏洞,它只允许在过去的截止日期前开始一个项目。但在其他实现中,它可能会产生更严重的后果。例如,如果攻击者在代币合约上利用此漏洞,它可能允许他们花费比他们拥有的更多的代币。
我在 Solidity 0.6.0 中编译了这段代码,没有错误(我正在使用 Remix)。但是突然间 SafeMath 库被弃用了,因为不再需要导入它而且我不知道如何修复这一行:
uint raiseUntil = now.add(durationInDays.mul(1 天));
我使用 ¨mul¨ 函数计算未来日期。
我把下面的所有代码都给你。它是众筹平台的后端。 raisedUntil变量是众筹项目截止日期。
pragma solidity 0.6.0;
// Importing OpenZeppelin's SafeMath Implementation
import 'https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Crowdfunding {
using SafeMath for uint256;
// List of existing projects
Project[] private projects;
// Event that will be emitted whenever a new project is started
event ProjectStarted(
address contractAddress,
address projectStarter,
string projectTitle,
string projectDesc,
uint256 deadline,
uint256 goalAmount
);
/** @dev Function to start a new project.
* @param title Title of the project to be created
* @param description Brief description about the project
* @param durationInDays Project deadline in days
* @param amountToRaise Project goal in wei
*/
function startProject(
string calldata title,
string calldata description,
uint durationInDays,
uint amountToRaise
) external {
uint raiseUntil = now.add(durationInDays.mul(1 days));
Project newProject = new Project(msg.sender, title, description, raiseUntil, amountToRaise);
projects.push(newProject);
emit ProjectStarted(
address(newProject),
msg.sender,
title,
description,
raiseUntil,
amountToRaise
);
}
/** @dev Function to get all projects' contract addresses.
* @return A list of all projects' contract addreses
*/
function returnAllProjects() external view returns(Project[] memory){
return projects;
}
}
contract Project {
using SafeMath for uint256;
// Data structures
enum State {
Fundraising,
Expired,
Successful
}
// State variables
address payable public creator;
uint public amountGoal; // required to reach at least this much, else everyone gets refund
uint public completeAt;
uint256 public currentBalance;
uint public raiseBy;
string public title;
string public description;
State public state = State.Fundraising; // initialize on create
mapping (address => uint) public contributions;
// Event that will be emitted whenever funding will be received
event FundingReceived(address contributor, uint amount, uint currentTotal);
// Event that will be emitted whenever the project starter has received the funds
event CreatorPaid(address recipient);
// Modifier to check current state
modifier inState(State _state) {
require(state == _state);
_;
}
// Modifier to check if the function caller is the project creator
modifier isCreator() {
require(msg.sender == creator);
_;
}
constructor
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint fundRaisingDeadline,
uint goalAmount
) public {
creator = projectStarter;
title = projectTitle;
description = projectDesc;
amountGoal = goalAmount;
raiseBy = fundRaisingDeadline;
currentBalance = 0;
}
/** @dev Function to fund a certain project.
*/
function contribute() external inState(State.Fundraising) payable {
require(msg.sender != creator);
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
currentBalance = currentBalance.add(msg.value);
emit FundingReceived(msg.sender, msg.value, currentBalance);
checkIfFundingCompleteOrExpired();
}
/** @dev Function to change the project state depending on conditions.
*/
function checkIfFundingCompleteOrExpired() public {
if (currentBalance >= amountGoal) {
state = State.Successful;
payOut();
} else if (now > raiseBy) {
state = State.Expired;
}
completeAt = now;
}
/** @dev Function to give the received funds to project starter.
*/
function payOut() internal inState(State.Successful) returns (bool) {
uint256 totalRaised = currentBalance;
currentBalance = 0;
if (creator.send(totalRaised)) {
emit CreatorPaid(creator);
return true;
} else {
currentBalance = totalRaised;
state = State.Successful;
}
return false;
}
/** @dev Function to retrieve donated amount when a project expires.
*/
function getRefund() public inState(State.Expired) returns (bool) {
require(contributions[msg.sender] > 0);
uint amountToRefund = contributions[msg.sender];
contributions[msg.sender] = 0;
if (!msg.sender.send(amountToRefund)) {
contributions[msg.sender] = amountToRefund;
return false;
} else {
currentBalance = currentBalance.sub(amountToRefund);
}
return true;
}
/** @dev Function to get specific information about the project.
* @return Returns all the project's details
*/
function getDetails() public view returns
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint256 deadline,
State currentState,
uint256 currentAmount,
uint256 goalAmount
) {
projectStarter = creator;
projectTitle = title;
projectDesc = description;
deadline = raiseBy;
currentState = state;
currentAmount = currentBalance;
goalAmount = amountGoal;
}
}
您可能决定切换到较新版本的 Solidity(当前为 0.8.4),其中 now
是 deprecated,所以我已经使用 block.timestamp
。这是同一件事 - now
只是它的别名。
SafeMath 库检查两个无符号整数的算术运算(例如乘法或加法)是否 overflow/underflow。如果会,它会抛出异常。如果不是,则执行算术运算。由于 Solidity 0.8 这是自动完成的并且不需要 SafeMath,因此使用当前版本的另一个原因。
在 Solidity 0.8 和更新版本中,如果这会溢出,它会自动抛出异常
uint raiseUntil = block.timestamp + durationInDays * 1 days;
在旧版本中,您需要自己检查可能的溢出并从代码中抛出异常(与 SafeMath 的行为相同)。
uint durationInSeconds = durationInDays * 1 days;
// Check that the result of division (inverse operation to multiplication) is the original number.
// If it's not, throw an exception, because the multiplication overflowed.
require(durationInSeconds / durationInDays == 1 days, 'Multiplication overflow');
uint raiseUntil = block.timestamp + durationInSeconds;
// Check that the result of subtraction (inverse operation to addition) is the original number.
// If it's not, throw an exception, because the addition overflowed.
require(raiseUntil - block.timestamp == durationInSeconds, 'Addition overflow');
Integer overflow/underflow 是智能合约中最常见的安全漏洞之一。如果您的 startProject()
函数存在漏洞,它只允许在过去的截止日期前开始一个项目。但在其他实现中,它可能会产生更严重的后果。例如,如果攻击者在代币合约上利用此漏洞,它可能允许他们花费比他们拥有的更多的代币。