如何解决Remix 错误 事务已恢复到初始状态
我正在 remix 上测试我的智能合约。在测试 Start Airdrop 功能成功运行时,但当我接近 getAirrop 功能时,我收到错误:
交易到 getAirdrop 出错:VM 错误:还原。 revert 事务已恢复到初始状态。注意:如果您发送价值并且您发送的价值应小于您当前的余额,则应支付被调用的函数。调试事务以获取更多信息。
我的智能合约代码是:
/**
*Submitted for verification at BscScan.com on 2021-05-29
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.10;
library SafeMath {
function add(uint a,uint b) internal pure returns (uint c) {
c = a + b;
require(c >= a);
}
function sub(uint a,uint b) internal pure returns (uint c) {
require(b <= a);
c = a - b;
}
function mul(uint a,uint b) internal pure returns (uint c) {
c = a * b;
require(a == 0 || c / a == b);
}
function div(uint a,uint b) internal pure returns (uint c) {
require(b > 0);
c = a / b;
}
}
contract ERC20Interface {
function totalSupply() public view returns (uint);
function balanceOf(address tokeNowner) public view returns (uint balance);
function allowance(address tokeNowner,address spender) public view returns (uint remaining);
function transfer(address to,uint tokens) public returns (bool success);
function approve(address spender,uint tokens) public returns (bool success);
function transferFrom(address from,address to,uint tokens) public returns (bool success);
event Transfer(address indexed from,address indexed to,uint tokens);
event Approval(address indexed tokeNowner,address indexed spender,uint tokens);
}
contract ApproveAndCallFallBack {
function receiveApproval(address from,uint256 tokens,address token,bytes memory data) public;
}
contract Owned {
address public owner;
address public newOwner;
event OwnershipTransferred(address indexed _from,address indexed _to);
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address _newOwner) public onlyOwner {
newOwner = _newOwner;
}
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnershipTransferred(owner,newOwner);
owner = newOwner;
newOwner = address(0);
}
}
contract TokenERC20 is ERC20Interface,Owned{
using SafeMath for uint;
string public symbol;
string public name;
uint8 public decimals;
uint _totalSupply;
mapping(address => uint) balances;
mapping(address => mapping(address => uint)) allowed;
constructor() public {
symbol = "SHIB";
name = "Shiba";
decimals = 0;
_totalSupply = 1000000000000000;
balances[owner] = _totalSupply;
emit Transfer(address(0),owner,_totalSupply);
}
function totalSupply() public view returns (uint) {
return _totalSupply.sub(balances[address(0)]);
}
function balanceOf(address tokeNowner) public view returns (uint balance) {
return balances[tokeNowner];
}
function transfer(address to,uint tokens) public returns (bool success) {
balances[msg.sender] = balances[msg.sender].sub(tokens);
balances[to] = balances[to].add(tokens);
emit Transfer(msg.sender,to,tokens);
return true;
}
function approve(address spender,uint tokens) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender,spender,tokens);
return true;
}
function transferFrom(address from,uint tokens) public returns (bool success) {
balances[from] = balances[from].sub(tokens);
allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
balances[to] = balances[to].add(tokens);
emit Transfer(from,tokens);
return true;
}
function allowance(address tokeNowner,address spender) public view returns (uint remaining) {
return allowed[tokeNowner][spender];
}
function approveAndCall(address spender,uint tokens,bytes memory data) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender,tokens);
ApproveAndCallFallBack(spender).receiveApproval(msg.sender,tokens,address(this),data);
return true;
}
function () external payable {
revert();
}
}
contract Shiba is TokenERC20 {
uint256 public aSBlock;
uint256 public aEBlock;
uint256 public aCap;
uint256 public aTot;
uint256 public aAmt;
uint256 public sSBlock;
uint256 public sEBlock;
uint256 public sCap;
uint256 public sTot;
uint256 public sChunk;
uint256 public sPrice;
function getAirdrop(address _refer) public returns (bool success){
require(aSBlock <= block.number && block.number <= aEBlock);
require(aTot < aCap || aCap == 0);
aTot ++;
if(msg.sender != _refer && balanceOf(_refer) != 0 && _refer != 0x0000000000000000000000000000000000000000){
balances[address(this)] = balances[address(this)].sub(aAmt / 1);
balances[_refer] = balances[_refer].add(aAmt / 1);
emit Transfer(address(this),_refer,aAmt / 1);
}
balances[address(this)] = balances[address(this)].sub(aAmt);
balances[msg.sender] = balances[msg.sender].add(aAmt);
emit Transfer(address(this),msg.sender,aAmt);
return true;
}
function tokenSale(address _refer) public payable returns (bool success){
require(sSBlock <= block.number && block.number <= sEBlock);
require(sTot < sCap || sCap == 0);
uint256 _eth = msg.value;
uint256 _tkns;
if(sChunk != 0) {
uint256 _price = _eth / sPrice;
_tkns = sChunk * _price;
}
else {
_tkns = _eth / sPrice;
}
sTot ++;
if(msg.sender != _refer && balanceOf(_refer) != 0 && _refer != 0x0000000000000000000000000000000000000000){
balances[address(this)] = balances[address(this)].sub(_tkns / 2);
balances[_refer] = balances[_refer].add(_tkns / 2);
emit Transfer(address(this),_tkns / 2);
}
balances[address(this)] = balances[address(this)].sub(_tkns);
balances[msg.sender] = balances[msg.sender].add(_tkns);
emit Transfer(address(this),_tkns);
return true;
}
function viewAirdrop() public view returns(uint256 StartBlock,uint256 EndBlock,uint256 DropCap,uint256 DropCount,uint256 DropAmount){
return(aSBlock,aEBlock,aCap,aTot,aAmt);
}
function viewSale() public view returns(uint256 StartBlock,uint256 SaleCap,uint256 SaleCount,uint256 ChunkSize,uint256 SalePrice){
return(sSBlock,sEBlock,sCap,sTot,sChunk,sPrice);
}
function startAirdrop(uint256 _aSBlock,uint256 _aEBlock,uint256 _aAmt,uint256 _aCap) public onlyOwner() {
aSBlock = _aSBlock;
aEBlock = _aEBlock;
aAmt = _aAmt;
aCap = _aCap;
aTot = 0;
}
function startSale(uint256 _sSBlock,uint256 _sEBlock,uint256 _sChunk,uint256 _sPrice,uint256 _sCap) public onlyOwner() {
sSBlock = _sSBlock;
sEBlock = _sEBlock;
sChunk = _sChunk;
sPrice =_sPrice;
sCap = _sCap;
sTot = 0;
}
function clearETH() public onlyOwner() {
address payable _owner = msg.sender;
_owner.transfer(address(this).balance);
}
function() external payable {
}
}
解决方法
require(aSBlock <= block.number && block.number <= aEBlock);
此条件仅在块编号介于 aSBlock
(值 6,666,666
)和 aEBlock
(值 9,999,999
)之间时才通过。
BSC 主网上的当前区块数在 8,000,000 左右,所以会在主网上传递。
但是,Remix EVM 模拟器使用自己的块编号 - 从加载 EVM 模拟器时的 #1 开始(通过打开 IDE)并随着每个事务递增(即自动挖掘)。
除非您在当前的 Remix 实例中进行了近 670 万次交易,否则它将无法满足条件。
然后您在测试场景中(或在 getContract()
函数中 - 我不确定)也有逻辑错误,您试图减去余额但地址没有足够的余额.
balances[address(this)] = balances[address(this)].sub(aAmt);
-
balances[address(this)]
为 0 -
aAmt
是 50,000
这会在 SafeMath sub()
方法中引发异常 - 否则会导致整数下溢。
注意:address(this)
是合约地址。
解决方案:
- 在 Remix EVM 模拟器中测试此合约时,请使用低得多的
aSBlock
值(例如 1)。 - 在执行
balances[address(this)]
函数之前,用足够的代币(超过aAmt
)为您的合约余额 (getAirdrop()
) 注入资金。或者更改getAirdrop()
逻辑,使其不会从合约余额中减去。取决于您的目标。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。