Die ERC20-Übertragung wird zurückgesetzt, wenn sie von einem Smart Contract aufgerufen wird

Ich habe einen ERC20-Token MyToken, der gut zu funktionieren scheint. Ich habe einen weiteren Smart Contract AirDrop, der MyToken verteilen soll. Die sendTokens()Methode nimmt ein Array von Adressen und ein Array von Werten und verteilt valuesToken an addresses.

Oder zumindest soll das passieren. sendTokens()anruft sendInternally(), die dann anruft ERC20(token).transfer(). Wenn ich dies auf Trüffel ausführe, erhalte ich immer eine Fehlermeldung mit der Meldung: Error: VM Exception while processing transaction: revert.

Ich habe Truffle debug verwendet, um weitere Untersuchungen durchzuführen, und festgestellt, dass die Zeile, die die eigentliche Übertragung ERC20(token).transfer()durchführt, einen Fehler auslöst. Wenn Sie den Debugger schrittweise verwenden, scheint es, als ob Sie nur zu dieser Zeile gelangen und aus einem unbekannten Grund einen Fehler ausgeben. Einen konkreten Grund kann ich nicht feststellen.

Um sicherzustellen, dass das Problem nicht mit der Methode selbst zu tun transferhat, habe ich versucht, aufzurufen, MyToken.transferAnyERC20Token()die auch verwendet ERC20(token).transfer(). Das dort drüben funktioniert gut, aber der scheinbar gleiche Aufruf im Airdrop-Vertrag schlägt fehl. Der AirDrop-Vertrag wird mit der Adresse von initialisiertMyToken

Ich habe dies auch bei Ganache und Rinkeby mit den gleichen Ergebnissen versucht.

Ich habe den gesamten Code unten gepostet.:

library SafeMath {

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

contract ERC20 {
    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant 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 Ownable {
    address public owner;


    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

contract StandardToken is ERC20  {

  using SafeMath for uint256;

  mapping (address => mapping (address => uint256)) internal allowed;

  mapping(address => uint256) public balances;

  uint256 _totalSupply;

  function totalSupply() public view returns (uint256) {
    return _totalSupply;
  }

  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balances[_owner];
  }

  function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);

    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    emit Transfer(_from, _to, _value);
    return true;
  }

  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    emit Approval(msg.sender, _spender, _value);
    return true;
  }

  function allowance(address _owner, address _spender) public view returns (uint256) {
    return allowed[_owner][_spender];
  }

  function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
    allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

  function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
    uint oldValue = allowed[msg.sender][_spender];
    if (_subtractedValue > oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

}

Hier ist der Code für MyToken:

contract MyToken is StandardToken, Ownable {
    using SafeMath for uint256;

    uint256 public constant TOTAL_SUPPLY = 10 ** 9;

    string public constant name = "My Token";
    string public constant symbol = "MYT";
    uint8 public constant decimals = 18;

    mapping (address => string) aliases;
    mapping (string => address) addresses;

    constructor() public {
        _totalSupply = TOTAL_SUPPLY * (10**uint256(decimals));
        balances[owner] = _totalSupply;
        emit Transfer(address(0), owner, _totalSupply);
    }

    function availableSupply() public view returns (uint256) {
        return _totalSupply.sub(balances[owner]).sub(balances[address(0)]);
    }

    function approveAndCall(address spender, uint256 tokens, bytes data) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data);
        return true;
    }

    function () public payable {
        revert();
    }

    function transferAnyERC20Token(address tokenAddress, uint256 tokens) public onlyOwner returns (bool success) {
        return ERC20(tokenAddress).transfer(owner, tokens);
    }

}

Und der Airdrop-Code:

contract AirDrop is Ownable {

  ERC20 public token;
  address public tokenWallet;
  address public tokenAddress;

  event TransferredToken(address indexed to, uint256 value);
  event FailedTransfer(address indexed to, uint256 value);

  constructor(address _tokenAddress, address _tokenWallet) public {
      tokenWallet = _tokenWallet;
      tokenAddress = _tokenAddress;
      token = ERC20(_tokenAddress);
  }

  function sendTokens(address[] destinations, uint256[] values, address _tokenAddress, address _tokenWallet) onlyOwner external {
      require(destinations.length == values.length);
      uint256 i = 0;
      while (i < destinations.length) {
          uint256 toSend = values[i] * 10**18;
          sendInternally(destinations[i], toSend, values[i]);
          i++;
      }
  }

  function sendTokensSingleValue(address[] destinations, uint256 value) onlyOwner external {
      uint256 i = 0;
      uint256 toSend = value * 10**18;
      while (i < destinations.length) {
          sendInternally(destinations[i] , toSend, value);
          i++;
      }
  }

  function sendInternally(address recipient, uint256 tokens, uint256 valueToPresent) internal {
    require(recipient != address(0));
    ERC20(tokenAddress).transfer(recipient, tokens);
  }


  function tokensAvailable() constant returns (uint256) {
    return token.allowance(tokenWallet, msg.sender);
  }

  function destroy() onlyOwner public {
    selfdestruct(owner);
  }
}
Übertragen Sie nach der Initialisierung AirDropmit der Adresse von MyTokeneinen der Token an die AirDropAdresse, damit sie etwas auf ihrem Konto verteilen kann? MyToken.transferAnyERC20Token()Beachten Sie , dass beim Aufruf transfervon Ihre Adresse als lautet msg.sender, während es sich im anderen Fall um die Adresse der AirDropbereitgestellten Instanz handelt, die möglicherweise 0 Token auf ihrem Kontostand hat.
! Bildbeschreibung hier eingeben Probleme lösen Ich stehe vor diesem Problem können Sie mir helfen

Antworten (2)

Ich habe versucht, MyToken.transferAnyERC20Token() aufzurufen, das auch ERC20(token).transfer() verwendet. Das dort drüben funktioniert gut, aber der scheinbar gleiche Aufruf im Airdrop-Vertrag schlägt fehl.

Ich bin mir nicht 100 % sicher, dass dies die Lösung ist, aber die Art und Weise, wie Sie Ihren Prozess beschrieben haben, deutet auf eine gewisse Verwirrung darüber hin, wo sich die Token befinden sollten.

Wenn Sie manuell testen, muss Ihr externes Konto, das die Transaktion signiert, über Token verfügen, um sie zu senden. Gleiches gilt für den Airdrop. Es kann nicht senden, was es nicht hat.

Haben Sie daran gedacht, vom Deployer-/Minter-Konto auf den Airdrop-Vertrag zu übertragen, bevor Sie den Airdrop-Vertrag anweisen, Token zu senden, die er nicht hat?

Falls es nicht klar ist.

  1. Alice setzt die Verträge ein und Alice hat (normalerweise) 100 % des ursprünglichen Angebots.
  2. Alice möchte, dass der Airdrop-Vertrag Token verteilt, also überträgt Alice genügend Token an den Airdrop-Vertrag.
  3. Alice unterzeichnet eine Transaktion mit dem Airdrop-Vertrag und weist ihn an, Token in der Verwahrung des Airdrop-Vertrags an die Empfänger weiterzuleiten.

Die OpenZeppelin-Implementierung von ERC20, die Sie anscheinend verwenden, löst eine Ausnahme aus, falls der Absender (der Airdrop-Vertrag) nicht genügend Token hat, aus denen er für die transferMethode ziehen kann.

Ich hoffe es hilft.

Das ist absolut richtig ... Ich habe vergessen, die Token zum Airdrop zu übertragen. Das Problem war, dass der Airdrop-Vertrag derjenige ist, der die Übertragung ausführt und nicht über die zu sendenden Token verfügt. Für andere, die dieses Problem in Zukunft haben könnten, gibt es 2 Möglichkeiten: 1. Entweder Token an die AirDropAdresse übertragen, damit der Airdrop Token senden kann. 2. Rufen MyToken.approve(AirDrop.address)Sie vom Konto des Token-Inhabers auf, um die Airdrop-Adresse zum Senden von Token zu genehmigen, und verwenden Sie dann ERC20(address).transferFrom()stattdessen fotransfer

Dieser Fehler: VM Exception while processing transaction: reverttritt auf, wenn diese Anweisungen nicht ausgewertet werdentrue

require(msg.sender == owner);
require(_to != address(0));
require(_value <= balances[msg.sender]);

und etc... , Sie haben viele oder requires in Ihrem Vertrag

hast du auch reverthier:

function () public payable {
    revert();
}

Es hängt also alles von der Eingabe ab, die Sie dem Vertrag geben. Revert ist so etwas wie assertin C/C++, wo das Programm einfach existiert. Gleiches passiert hier in der EVM, der Vertrag wird wieder ausgeführt. Finden Sie heraus, welche Eingabe ungültig ist, und dies wird die Lösung für Ihr Problem sein.