我有一个合约GetNewToken接收OldToken并通过函数buyTokens提供NewToken
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import './NewToken.sol';
import './OldToken.sol';
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract GetNewToken {
string public name = "Get New Token Contract";
address public owner;
NewToken public newToken;
OldToken public oldToken;
event Bought(address buyer, uint256 amount);
event TransferFromFailed(uint256 amount);
constructor(NewToken _newToken, OldToken _oldToken) public {
newToken = _newToken;
oldToken = _oldToken;
owner = msg.sender;
}
function buyTokens(uint256 _amount) public {
require(_amount > 0, "amount can not be 0");
uint256 userBalance = oldToken.balanceOf(msg.sender);
require(userBalance >= _amount, "Not enough Old Token in your account");
bool success = oldToken.transferFrom(msg.sender, address(this), _amount);
if(success) {
uint256 newBalance = newToken.balanceOf(address(this));
require(_amount <= newBalance, "Not enough New Tokens in the contract");
newToken.transfer(msg.sender, _amount);
emit Bought(msg.sender, _amount);
} else {
emit TransferFromFailed(_amount);
revert("oldToken.transferFrom function failed");
}
}
}
OldTokens和NewTokens是标准的 ERC20 实现。其中一个如下所示(除名称和符号外,其他完全相同)
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract OldToken {
string public name = "Old Token";
string public symbol = "OLDT";
uint256 public totalSupply = 1000000000000000000000000; // 1 million tokens
uint8 public decimals = 18;
event Transfer(
address indexed _from,
address indexed _to,
uint256 _value
);
event Approval(
address indexed _owner,
address indexed _spender,
uint256 _value
);
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
constructor() public {
balanceOf[msg.sender] = totalSupply;
}
function transfer(address _to, uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value);
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= balanceOf[_from]);
require(_value <= allowance[_from][msg.sender]);
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
allowance[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
}
2_deployed_token.js如下所示。它还将一些NewToken转移到GetNewToken合约,并将一些OldToken转移到账户[1],然后将用于调用buyTokens函数。
const NewToken = artifacts.require("NewToken");
const OldToken = artifacts.require("OldToken");
const GetNewToken = artifacts.require("GetNewToken");
module.exports = async function(deployer, network, accounts) {
await deployer.deploy(NewToken);
const newToken = await NewToken.deployed();
await deployer.deploy(OldToken);
const oldToken = await OldToken.deployed();
await deployer.deploy(GetNewToken, newToken.address, oldToken.address);
const getNewToken = await GetNewToken.deployed();
await newToken.transfer(GetNewToken.address, "1000000000000000000000"); // 1000 tokens
await oldToken.transfer(accounts[1], "1000000000000000000000"); // 1000 tokens
};
truffle-config.js看起来像:
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*"
}
},
contracts_directory: './contracts/',
contracts_build_directory: './abis/',
compilers: {
solc: {
version: "0.6.2",
optimizer: {
enabled: true,
runs: 200
},
evmVersion: "petersburg"
}
}
};
现在编译和迁移工作正常(我使用 ganache 作为本地区块链):
truffle compile
truffle migrate --reset
地址如下(在 ganache 中): OldToken 0xaA47e5555db895b230A16AF6860bf5DF442C7dB9
新令牌 0x43C68Efa858a9eA2F2DdD4e1AbDeA90a96f5C56E
GetNewToken 0x4b4a5d3Ad0ed472876e50FB0daa6ED2FA78488e2
以下是随后在 truffle 控制台中执行的步骤:
NewToken.deployed().then(function(instance){newToken = instance})
OldToken.deployed().then(function(instance){oldToken = instance})
GetNewToken.deployed(newToken, oldToken).then(function(instance){return instance.buyTokens('1000000000000000000', {from:accounts[1]});});
然后返回错误:
Uncaught Error: Returned error: VM Exception while processing transaction: revert
at evalmachine.<anonymous>:0:75
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
hijackedStack: 'Error: Returned error: VM Exception while processing transaction: revert\n' +
' at Object.ErrorResponse (/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/src/errors.js:29:1)\n' +
' at /usr/local/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-requestmanager/src/index.js:140:1\n' +
' at /usr/local/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:112:1\n' +
' at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/web3-providers-http/src/index.js:96:1)\n' +
' at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)\n' +
' at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)\n' +
' at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)\n' +
' at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:47)\n' +
' at IncomingMessage.emit (events.js:326:22)\n' +
' at IncomingMessage.EventEmitter.emit (domain.js:548:15)\n' +
' at endReadableNT (_stream_readable.js:1252:12)\n' +
' at processTicksAndRejections (internal/process/task_queues.js:80:21)'
使用truffle debug 0xdf7af41dd709d19a75b497c25cde854328ea50176c35c6661416a316c382d9ad调试事务哈希并按o突出显示GetNewToken函数buyTokens行下方
debug(development:0xdf7af41d...)> o
Transaction halted with a RUNTIME ERROR.
There was no revert message. This may be due to an in intentional halting expression, such as assert(), revert(), or require(), or could be due to an unintentional exception such as out-of-gas exceptions.
Please inspect your transaction parameters and contract code to determine the meaning of this error.
Stacktrace:
Error: Revert or exceptional halt
at unknown function [address 0xaA47e5555db895b230A16AF6860bf5DF442C7dB9] (/Users/someuser/projects/project-folder/contracts/GetNewToken.sol:40:24)
at GetNewToken.buyTokens [address 0x4b4a5d3Ad0ed472876e50FB0daa6ED2FA78488e2] (/Users/someuser/projects/project-folder/contracts/GetNewToken.sol:40:24)
at GetNewToken [address 0x4b4a5d3Ad0ed472876e50FB0daa6ED2FA78488e2] (/Users/someuser/projects/project-folder/contracts/GetNewToken.sol:27:5)
Location of error:
GetNewToken.sol:
40: bool success = oldToken.transferFrom(msg.sender, address(this), _amount);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
debug(development:0xdf7af41d...)> o
Transaction has halted; cannot advance.
请忽略行号,因为我在 GetNewToken.sol 文件中几乎没有评论
为什么我无法使用 transferFrom?账户[1] 已经有 OldTokens(根据迁移)。
另外,当我使用松露控制台时
truffle(development)> OldToken.deployed().then(function(instance){oldToken = instance})
truffle(development)> let a = oldToken.balanceOf(accounts[1])
undefined
truffle(development)> a
BN {
negative: 0,
words: [ 44040192, 40595831, 222044, <1 empty item> ],
length: 3,
red: null
}
为什么我没有得到 1000000000000000000000(根据迁移脚本)?
谢谢!