Contract-Development

是什麼導致所需的氣體超過限額或總是交易失敗?

  • March 2, 2018

這可能看起來重複,但我找不到正確的答案。我的目標是開發一個合約,合約所有者可以設置收件人和他們可以提取的代幣數量。設置完成後,接收者可以呼叫帶有帶有 gas 價格的元遮罩的函式並贖回代幣。

  • 已通過 Remix 部署了以下合約(代幣所有者與合約所有者不同)
  • 當嘗試執行setBalances每個數組中只有 2 個項目的方法時,導致錯誤transact to WithDraw.setBalances errored: Error: gas required exceeds allowance or always failing transaction
  • 已將氣體限制更改為900000000000但結果仍然相同。
  • 只是為了檢查,呼叫redeem它也會產生相同的錯誤transact to WithDraw.redeem errored: Error: gas required exceeds allowance or always failing transaction

智能合約程式碼

pragma solidity ^0.4.18;

/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {

 address public owner;
 event OwnershipTransferred (address indexed _from, address indexed _to);

 /**
  * @dev The Ownable constructor sets the original `owner` of the contract to the sender
  * account.
  */
 function Ownable() public{
   owner = msg.sender;
   OwnershipTransferred(address(0), owner);
 }

 /**
  * @dev Throws if called by any account other than the owner.
  */
 modifier onlyOwner() {
   require(msg.sender == owner);
   _;
 }

 /**
  * @dev Allows the current owner to transfer control of the contract to a newOwner.
  * @param newOwner The address to transfer ownership to.
  */
 function transferOwnership(address newOwner) public onlyOwner {
   require(newOwner != address(0));
   owner = newOwner;
   OwnershipTransferred(owner,newOwner);
 }
}

/**
* @title Token
* @dev API interface for interacting with the Token contract 
*/
interface Token {
 function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
 function balanceOf(address _owner) constant external returns (uint256 balance);
}

contract WithDraw is Ownable {

 Token token;
 mapping(address => uint256) public redeemBalanceOf;
 event BalanceSet(address indexed beneficiary, uint256 value);
 event Redeemed(address indexed beneficiary, uint256 value);

 function WithDraw() public {
     address _tokenAddr = MY_TOKEN_ADDR;
     token = Token(_tokenAddr);
 }

 function setBalances(address[] dests, uint256[] values) onlyOwner public {
   uint256 i = 0;
   while (i < dests.length) {
       if(dests[i] == address(0)) continue;
       uint256 toSend = values[i] * 10**18;
       redeemBalanceOf[dests[i]] += toSend;
       i++;
       BalanceSet(dests[i],values[i]);
   }
 }

 function redeem(uint256 quantity) external{
     uint256 baseUnits = quantity * 10**18;
     require(redeemBalanceOf[msg.sender]>=baseUnits);
     redeemBalanceOf[msg.sender] -= baseUnits;
     token.transferFrom(owner,msg.sender,baseUnits);
     Redeemed(msg.sender,quantity);
 }

}

錯誤的原因是什麼?合約所有者是否應該與代幣合約所有者相同?

編輯1

根據@smarx 回复,更改瞭setBalances如下方法。

 function setBalances(address[] dests, uint256[] values) onlyOwner public {
   for (uint256 i = 0; i < dests.length; i++) {
       if(dests[i] != address(0)) 
       {
           uint256 toSend = values[i] * 10**18;
           redeemBalanceOf[dests[i]] += toSend;
           BalanceSet(dests[i],values[i]);
       }
       i++;
   }
 }

現在,當我將數組中的兩個項目傳遞給設置值時,我只看到第一個項目的事件日誌,而第二個項目被遺漏了。使困惑!​​。for循環有什麼問題?

除了循環問題之外,即使傳遞的數量少於已分配的數量,呼叫該redeem函式也會引發相同的錯誤。transact to WithDraw.redeem errored: Error: gas required exceeds allowance or always failing transaction是否需要在該approve方法中進行令牌傳輸之前呼叫該方法?

編輯2

不能解決 for 循環的問題。將其修改回 while back with 批准功能,如下所示。這次setBalances工作正常,餘額顯示正常。

function setBalances(address[] dests, uint256[] values) onlyOwner public {
   uint256 i = 0;
   while (i < dests.length){
       if(dests[i] != address(0)) 
       {
           uint256 toSend = values[i] * 10**18;
           redeemBalanceOf[dests[i]] += toSend;
           token.approve(dests[i], toSend);
           BalanceSet(dests[i],values[i]);
       }
       i++;
   }
 }

但是,仍然redeem拋出同樣的錯誤。非常感謝任何幫助。

編輯 3

進行瞭如下更改。並且這次已經向這個合約地址轉移了足夠數量的代幣

 function setBalances(address[] dests, uint256[] values) onlyOwner public {
   uint256 i = 0; 
   while (i < dests.length){
       if(dests[i] != address(0)) 
       {
           uint256 toSend = values[i] * 10**18;
           redeemBalanceOf[dests[i]] += toSend;
           BalanceSet(dests[i],values[i]);
       }
       i++;
   }
 }

 function redeem(uint256 quantity) external{
     uint256 baseUnits = quantity * 10**18;
     uint256 tokensAvailable = token.balanceOf(this);
     require(redeemBalanceOf[msg.sender]>=baseUnits);
     require( tokensAvailable >= baseUnits);
     redeemBalanceOf[msg.sender] -= baseUnits;
     token.transfer(msg.sender,baseUnits);
     Redeemed(msg.sender,quantity);
 }

但仍然遇到同樣的問題。我肯定錯過了什麼,不知道是什麼。請幫忙。

雖然我不想回答自己的問題,但認為這對其他人會有所幫助。整個工作副本粘貼在下面。

  • 創建此契約
  • 有足夠的代幣轉移給它(如果您使用代幣合約的轉移方法,請確保遵循正確的小數,如果您看到足夠數量的代幣,請確保在 etherscan 中)

這對於可以設置合格代幣並要求參與者自行提款的空投公司很有用。

pragma solidity ^0.4.18;

/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {

 address public owner;
 event OwnershipTransferred (address indexed _from, address indexed _to);

 /**
  * @dev The Ownable constructor sets the original `owner` of the contract to the sender
  * account.
  */
 function Ownable() public{
   owner = msg.sender;
   OwnershipTransferred(address(0), owner);
 }

 /**
  * @dev Throws if called by any account other than the owner.
  */
 modifier onlyOwner() {
   require(msg.sender == owner);
   _;
 }

 /**
  * @dev Allows the current owner to transfer control of the contract to a newOwner.
  * @param newOwner The address to transfer ownership to.
  */
 function transferOwnership(address newOwner) public onlyOwner {
   require(newOwner != address(0));
   owner = newOwner;
   OwnershipTransferred(owner,newOwner);
 }
}

/**
* @title Token
* @dev API interface for interacting with the Token contract 
*/
interface Token {
 function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
 function balanceOf(address _owner) constant external returns (uint256 balance);
}

contract WithDraw is Ownable {

 Token token;
 mapping(address => uint256) public redeemBalanceOf;
 event BalanceSet(address indexed beneficiary, uint256 value);
 event Redeemed(address indexed beneficiary, uint256 value);

 function WithDraw() public {
     address _tokenAddr = MY_TOKEN_ADDR;
     token = Token(_tokenAddr);
 }

function setBalances(address[] dests, uint256[] values) onlyOwner public {
   uint256 i = 0; 
   while (i < dests.length){
       if(dests[i] != address(0)) 
       {
           uint256 toSend = values[i] * 10**18;
           redeemBalanceOf[dests[i]] += toSend;
           BalanceSet(dests[i],values[i]);
       }
       i++;
   }
 }

 function redeem(uint256 quantity) external{
     uint256 baseUnits = quantity * 10**18;
     uint256 senderEligibility = redeemBalanceOf[msg.sender];
     uint256 tokensAvailable = token.balanceOf(this);
     require(senderEligibility >= baseUnits);
     require( tokensAvailable >= baseUnits);
     if(token.transfer(msg.sender,baseUnits)){
       redeemBalanceOf[msg.sender] -= baseUnits;
       Redeemed(msg.sender,quantity);
     }
 }

 function removeBalances(address[] dests, uint256[] values) onlyOwner public {
   uint256 i = 0; 
   while (i < dests.length){
       if(dests[i] != address(0)) 
       {
           uint256 toRevoke = values[i] * 10**18;
           if(redeemBalanceOf[dests[i]]>=toRevoke)
           {
               redeemBalanceOf[dests[i]] -= toRevoke;
               BalanceCleared(dests[i],values[i]);
           }
       }
       i++;
   }

}

我認為你的錯誤的原因是你正在閱讀你的數組末尾:

while (i < dests.length) {
   // ...
   i++;
   // On the last iteration, i is dests.length, which is out of bounds.
   BalanceSet(dests[i],values[i]);
}

i++to 移到該BalanceSet行之後,或者更好的是,使用 for 循環:

for (uint256 i = 0; i < dests.length; i++) {
   // ...
   BalanceSet(dests[i], values[i]);
}

如果有人通過地址 0,這也將避免您目前擁有的無限循環。

引用自:https://ethereum.stackexchange.com/questions/41350