Was verursacht, dass das benötigte Gas den Freibetrag überschreitet oder die Transaktion immer fehlschlägt?

Dies kann doppelt aussehen, aber ich kann keine richtige Antwort finden. Mein Ziel ist es, einen Vertrag zu entwickeln, bei dem der Vertragsinhaber die Empfänger und die Anzahl der Token festlegen kann, die sie abheben können. Sobald sie eingestellt sind, kann der Empfänger eine Funktion mit Metamask aufrufen, die den Gaspreis trägt, und die Token einlösen.

  • Habe den folgenden Vertrag über Remix bereitgestellt (Token-Eigentümer ist nicht der Vertragseigentümer)
  • setBalancesBeim Versuch, die Methode mit nur 2 Elementen in jedem Array auszuführen , führte dies zu einem Fehlertransact to WithDraw.setBalances errored: Error: gas required exceeds allowance or always failing transaction
  • Habe das Gaslimit umgestellt 900000000000aber immer noch das gleiche Ergebnis.
  • Nur zur Überprüfung aufgerufen, redeemdie auch den gleichen Fehler ergebentransact to WithDraw.redeem errored: Error: gas required exceeds allowance or always failing transaction

Intelligenter Vertragscode

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);
  }

}

Was ist die Ursache des Fehlers? Sollte der Vertragsinhaber derselbe sein wie der Token-Vertragsinhaber?

EDIT1

Basierend auf der @smarx-Antwort haben Sie die setBalancesMethode wie unten geändert.

  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++;
    }
  }

Wenn ich jetzt zwei Elemente im Array übergebe, um den Wert festzulegen, sehe ich das Ereignisprotokoll nur für das erste Element, und das zweite wurde verpasst. Verwirrt!. Was ist mit der for-Schleife falsch?

Abgesehen von dem Schleifenproblem wird beim Aufrufen der redeemFunktion auch derselbe Fehler ausgegeben, transact to WithDraw.redeem errored: Error: gas required exceeds allowance or always failing transactionselbst wenn weniger als die bereits zugewiesene Menge übergeben wird. Muss die approveMethode vor der Tokenübertragung in dieser Methode aufgerufen werden?

EDIT2

Konnte das Problem nicht mit der for-Schleife lösen. Modifizierte es zurück zu While Back mit Genehmigungsfunktion wie unten. Diesmal setBalancesfunktionierte es richtig, die Salden wurden richtig angezeigt.

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++;
    }
  }

Aber immer noch, der redeemwirft den gleichen Fehler. Jede Hilfe wird sehr geschätzt.

BEARBEITEN 3

Habe die Änderungen wie unten vorgenommen. Und dieses Mal haben Sie eine ausreichende Anzahl von Token an diese Vertragsadresse übertragen

  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);
  }

Aber immer noch das gleiche Problem. Ich vermisse sicherlich etwas, weiß nicht, was es ist. Bitte helfen Sie.

Was ist der Zweck von if (dest[i] == address(0)) continue;? Das wird eine Endlosschleife verursachen, wenn es jemals passiert.
Hallo @smarx, Frage aktualisiert, bitte überprüfen und vorschlagen.
redeemAnrufe token.transferFrom(owner, ...). Vielleicht ownerhat approveda nicht genug Token, damit das funktioniert? (Versuchen Sie, diese Zeile auszukommentieren, um zu sehen, ob sie die Quelle des Reverts ist.)
Ich sehe das setBalancesauch Anrufe token.approve. Ich denke, es gibt einige Verwirrung darüber, wo sich die Token befinden. Es kann keinen Sinn machen, diese beiden Linien zu haben.
Die Token sind mit Verträgen ownerversehen, die die Auszahlung der Token für die Einlösefunktion genehmigen würden. Fehlt etwas?
Sie müssen aus entfernen token.approve( setBalancesda der Vertrag keine Token besitzt), und Sie müssen sicherstellen, dass die ownerAnrufe ausgeführt werden approve(<contract address>, <total amount>), bevor jemand versucht, anzurufen redeem.
Damit transferFromsie funktionieren, muss der Besitzer dieser Token approvezuerst ihre Übertragung durchführen. (In diesem Fall ownerist derjenige, der anrufen approvemuss, da er ownerdie Token besitzt.)
Ja, setBalanceskann nur vom Eigentümer aufgerufen werden, und der approveAufruf erfolgt in dieser Methode. Reicht das nicht?
Nein. Der Vertragsaufruf approvebewirkt nichts, da der Vertrag keine Token enthält. Der Eigentümer muss anrufen approve.
Bedeutet dies, dass es außerhalb dieses Soliditätsvertragscodes geschehen muss? oder ist es möglich, darin zu haben? Bitte stellen Sie das Snippet nach Möglichkeit in diesem Vertrag zur Verfügung.
Ja, es muss außerhalb dieses Vertrages geschehen.
Oder ist es möglich, innerhalb von Token an diesen Vertrag zu senden, setBalancesdamit die Einlösung einfach ist.
Der Vertrag kann nicht gewaltsam Token von jemandem nehmen. Der Eigentümer der Token muss transferdie Token bzw. approvediese übertragen.
Habe Änderungen vorgenommen, wie in EDIT 3 erwähnt , diesmal habe ich die Anzahl der Token auch an diese Verträge weitergegeben, erhalte aber immer noch den Fehler, wenn ich versuche, sie als eine andere Adresse einzulösen, die setBalance done war.
Dieser Code sieht für mich in Ordnung aus. Versuchen Sie, die Dinge einzeln zu kommentieren, um redeemzu sehen, was der Fehler ist. Es gibt zwei requires und ein transfer. Einer dieser drei Fehler fällt vermutlich aus, und das sollte Ihnen helfen, auf das Problem hinzuweisen.

Antworten (2)

Ich denke, die Ursache Ihres Fehlers ist, dass Sie über das Ende Ihrer Arrays hinaus lesen:

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

Verschieben Sie das i++to nach der BalanceSetZeile, oder noch besser, verwenden Sie eine for-Schleife:

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

Dadurch wird auch die Endlosschleife vermieden, die Sie derzeit haben, wenn jemand die Adresse 0 übergibt.

Danke smartx. Habe es in eine for-Schleife geändert und diese Zeile auch beibehalten, if(dests[i] == address(0)) continue;nur um die Übergabe der Adresse 0 zu verhindern. Dieses Mal ist der Fehler nicht aufgetreten, aber es wird redeemBalanceOfkein Wert für eines der Elemente angezeigt, die im Array übergeben wurden. Was fehlt nochmal?

Obwohl ich meine eigene Frage nicht beantworten möchte, dachte ich, es würde anderen helfen. Die gesamte Arbeitskopie wird unten eingefügt.

  • Erstellen Sie diesen Vertrag
  • Lassen Sie genügend Token übertragen (wenn Sie die Übertragungsmethode des Token-Vertrags verwenden, stellen Sie sicher, dass die richtigen Dezimalstellen eingehalten werden, und stellen Sie in Etherscan sicher, ob Sie eine ausreichende Anzahl von Token sehen).

Dies kann für Airdrop-Unternehmen nützlich sein, die die zulässigen Token festlegen und die Teilnehmer auffordern können, sich selbst zurückzuziehen.

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++;
    }

}