當從一個智能合約呼叫時,ERC20 轉賬會恢復
MyToken
我有一個似乎執行良好的 ERC20 代幣。我有另一個智能合約AirDrop
,應該分發 MyToken。該sendTokens()
方法接受一個地址數組和一個值數組並將values
令牌分發到addresses
.或者至少這是應該發生的。然後
sendTokens()
呼叫. 當我在松露上執行它時,我總是收到一條錯誤消息:.sendInternally()``ERC20(token).transfer()``Error: VM Exception while processing transaction: revert
我使用 Truffle debug 進行了進一步調查,發現執行實際傳輸的行會
ERC20(token).transfer()
引發錯誤。逐步使用調試器,它似乎只是到達該行並由於未知原因引發錯誤。我無法確定具體原因。為了確保問題與方法本身無關
transfer
,我嘗試呼叫MyToken.transferAnyERC20Token()
which also usesERC20(token).transfer()
. 那里工作得很好,但是空投合約中看似相同的呼叫出錯了。AirDrop 合約的初始化地址為MyToken
我也在 Ganache 和 Rinkeby 上嘗試過,結果相同。
我已經發布了下面的所有程式碼:
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; } }
這是程式碼
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); } }
以及空投程式碼:
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); } }
我嘗試呼叫 MyToken.transferAnyERC20Token(),它也使用 ERC20(token).transfer()。那里工作得很好,但是空投合約中看似相同的呼叫出錯了。
我不是 100% 確定這是解決方案,但是您描述流程的方式暗示了對令牌應該在哪裡的一些混淆。
當您手動測試時,您簽署交易的外部擁有的帳戶需要具有令牌才能發送它們。空投也是如此。它不能發送它沒有的東西。
你還記得在指示空投合約發送它沒有的代幣之前從部署者/鑄幣者賬戶轉移到空投合約嗎?
萬一不清楚。
- Alice 部署合約並且 Alice(通常)擁有 100% 的初始供應。
- Alice 希望 Airdrop 合約分發代幣,因此 Alice 將足夠的代幣轉移到 Airdrop 合約。
- Alice 向 Airdrop 合約簽署交易,指示其將 Airdrop 合約託管的代幣轉發給接收者。
您似乎正在使用的 ERC20 的 OpenZeppelin 實現將在發送方(即 Airdrop 合約)沒有足夠的代幣用於該
transfer
方法提取的情況下引發異常。希望能幫助到你。