I’ve a contract GetNewToken that receives OldToken and gives NewToken through the functions buyTokens
// 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 and NewTokens are standard ERC20 implementations. One of them is shown below (other is exactly similar with exception of name and symbol)
// 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;
}
}
The 2_deployed_token.js looks like below. It also transfers some NewToken to GetNewToken contract, and transfers some OldToken to accounts[1], which will then be used to call buyTokens function.
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 looks like:
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"
}
}
};
Now compile and migrate works fine (am using ganache as local blockchain):
truffle compile
truffle migrate --reset
The addresses are as follows (in ganache): OldToken 0xaA47e5555db895b230A16AF6860bf5DF442C7dB9
NewToken 0x43C68Efa858a9eA2F2DdD4e1AbDeA90a96f5C56E
GetNewToken 0x4b4a5d3Ad0ed472876e50FB0daa6ED2FA78488e2
Here are the steps then performed in truffle console:
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]});});
which then returns the error:
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)'
Debugging the transaction hash using truffle debug 0xdf7af41dd709d19a75b497c25cde854328ea50176c35c6661416a316c382d9ad and pressing o highlights below line of the GetNewToken function 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.
please ignore line numbers, as I had few comments in the GetNewToken.sol file
- Why I’m not able to use transferFrom? The accounts[1] already has OldTokens (as per migration).
- Also, when I use truffle console
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
}
why didn’t I get 1000000000000000000000 (as per migration script)?
Thanks!