Hi @Riki
You are missing the verification in transferFrom
, double check that one and be sure to verify:
- The allowance;
- The balance.
Cheers,
Dani
Hi @Riki
You are missing the verification in transferFrom
, double check that one and be sure to verify:
Cheers,
Dani
I see, thank you! This should be good to go then.
pragma solidity 0.8.0;
import "/.SafeMath.sol";
/**
* @title ERC20 standard token implementation.
* @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
*/
contract Token {
using Safemath for uint256;
/*
* Token name.
*/
string internal tokenName;
/*
* Token symbol.
*/
string internal tokenSymbol;
/*
* Number of decimals.
*/
uint8 internal tokenDecimals;
/*
* Total supply of tokens.
*/
uint256 internal tokenTotalSupply;
/*
* Balance information map.
*/
mapping (address => uint256) internal balances;
/*
* Token allowance mapping.
*/
mapping (address => mapping (address => uint256)) internal allowed;
/*
* @dev Trigger when tokens are transferred, including zero value transfers.
*/
event Transfer(address indexed _from,address indexed _to,uint256 _value);
/*
* @dev Trigger on any successful call to approve(address _spender, uint256 _value).
*/
event Approval(address indexed _owner,address indexed _spender,uint256 _value);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
tokenName = _name;
tokenSymbol = _symbol;
tokenDecimals = _decimals;
tokenTotalSupply = _initialOwnerBalance;
balances[msg.sender] = _initialOwnerBalance;
}
/*
* @dev Returns the name of the token.
*/
function name() external view returns (string memory _name){
_name = tokenName;
}
/*
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory _symbol){
_symbol = tokenSymbol;
}
/*
* @dev Returns the number of decimals the token uses.
*/
function decimals() external view returns (uint8 _decimals){
_decimals = tokenDecimals;
}
/*
* @dev Returns the total token supply.
*/
function totalSupply()external view returns (uint256 _totalSupply){
_totalSupply = tokenTotalSupply;
}
/*
* @dev Returns the account balance of another account with address _owner.
* @param _owner The address from which the balance will be retrieved.
*/
function balanceOf(address _owner) external view returns (uint256 _balance){
_balance = balances[_owner];
}
/*
* @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
* function SHOULD throw if the "from" account balance does not have enough tokens to spend.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transfer(address payable _to, uint256 _value) public returns (bool _success){
require(balances[msg.sender] >= _value, "not enough tokens bro");
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
_success = true;
}
/*
* @dev Allows _spender to withdraw from your account multiple times, up to the _value amount. If
* this function is called again it overwrites the current allowance with _value. SHOULD emit the Approval event.
* @param _spender The address of the account able to transfer the tokens.
* @param _value The amount of tokens to be approved for transfer.
*/
function approve(address _spender,uint256 _value) public returns (bool _success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
_success = true;
}
/*
* @dev Returns the amount which _spender is still allowed to withdraw from _owner.
* @param _owner The address of the account owning tokens.
* @param _spender The address of the account able to transfer the tokens.
*/
function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
_remaining = allowed[_owner][_spender];
}
/*
* @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
* Transfer event.
* @param _from The address of the sender.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
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] -=_value;
emit Transfer(_from, _to, _value);
_success = true;
}
}
This is my code.
pragma solidity 0.8.0;
import "./SafeMath.sol";
/**
* @title ERC20 standard token implementation.
* @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
*/
contract Token {
using SafeMath for uint256;
/*
* Token name.
*/
string internal tokenName;
/*
* Token symbol.
*/
string internal tokenSymbol;
/*
* Number of decimals.
*/
uint8 internal tokenDecimals;
/*
* Total supply of tokens.
*/
uint256 internal tokenTotalSupply;
/*
* Balance information map.
*/
mapping (address => uint256) internal balances;
/*
* Token allowance mapping.
*/
mapping (address => mapping (address => uint256)) internal allowed;
/*
* @dev Trigger when tokens are transferred, including zero value transfers.
*/
event Transfer(address indexed _from,address indexed _to,uint256 _value);
/*
* @dev Trigger on any successful call to approve(address _spender, uint256 _value).
*/
event Approval(address indexed _owner,address indexed _spender,uint256 _value);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
tokenName = _name;
tokenSymbol = _symbol;
tokenDecimals = _decimals;
tokenTotalSupply = _initialOwnerBalance;
balances[msg.sender] = _initialOwnerBalance;
}
/*
* @dev Returns the name of the token.
*/
function name() external view returns (string memory _name){
_name = tokenName;
}
/*
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory _symbol){
_symbol = tokenSymbol;
}
/*
* @dev Returns the number of decimals the token uses.
*/
function decimals() external view returns (uint8 _decimals){
_decimals = tokenDecimals;
}
/*
* @dev Returns the total token supply.
*/
function totalSupply()external view returns (uint256 _totalSupply){
_totalSupply = tokenTotalSupply;
}
/*
* @dev Returns the account balance of another account with address _owner.
* @param _owner The address from which the balance will be retrieved.
*/
function balanceOf(address _owner) external view returns (uint256 _balance){
_balance = balances[_owner];
}
/*
* @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
* function SHOULD throw if the "from" account balance does not have enough tokens to spend.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transfer(address payable _to, uint256 _value) public returns (bool _success){
require(balances[msg.sender] >= _value, "Not enough funds");
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/*
* @dev Allows _spender to withdraw from your account multiple times, up to the _value amount. If
* this function is called again it overwrites the current allowance with _value. SHOULD emit the Approval event.
* @param _spender The address of the account able to transfer the tokens.
* @param _value The amount of tokens to be approved for transfer.
*/
function approve(address _spender,uint256 _value) public returns (bool _success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/*
* @dev Returns the amount which _spender is still allowed to withdraw from _owner.
* @param _owner The address of the account owning tokens.
* @param _spender The address of the account able to transfer the tokens.
*/
function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
_remaining = allowed[_owner][_spender];
}
/*
* @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
* Transfer event.
* @param _from The address of the sender.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
require(allowed[_from][msg.sender] >= _value, "You are not allowed to transfer funds");
require(balances[_from] >= _value, "Not enough funds");
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(_from, _to, _value);
return true;
}
}
Hi @dan-i,
I am not actually sure by that. Shoudnāt be SafeMath require itself? Because SafeMath checks for underflow, so it canāt go under the 0. In other words you canāt spend more than your balance. I think you donāt need require in this case, isnāt it?
Also I have read from you earlier that in 0.8.0 operators + - : * comes with built-in under/overflow check. So basically you only need + - : * without require or SafeMath.
So where is catch? I guess fallback is not possible either, because fallback comes only with call/send/transfer? And fallback does not have enough of gas anyway in 0.8.0. So what is it? Thanks.
Here is my solution to the erc20 assignment.
The main idea is that msg.sender
is the owner when calling transfer()
and approve()
, and msg.sender
is the spender when calling transferFrom()
.
pragma solidity 0.8.0;
/**
* @title ERC20 standard token implementation.
* @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
*/
contract Token {
/*
* Token name.
*/
string internal tokenName;
/*
* Token symbol.
*/
string internal tokenSymbol;
/*
* Number of decimals.
*/
uint8 internal tokenDecimals;
/*
* Total supply of tokens.
*/
uint256 internal tokenTotalSupply;
/*
* Balance information map.
*/
mapping (address => uint256) internal balances;
/*
* Token allowance mapping.
*/
mapping (address => mapping (address => uint256)) internal allowed;
/*
* @dev Trigger when tokens are transferred, including zero value transfers.
*/
event Transfer(address indexed _from,address indexed _to,uint256 _value);
/*
* @dev Trigger on any successful call to approve(address _spender, uint256 _value).
*/
event Approval(address indexed _owner,address indexed _spender,uint256 _value);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
tokenName = _name;
tokenSymbol = _symbol;
tokenDecimals = _decimals;
tokenTotalSupply = _initialOwnerBalance;
balances[msg.sender] = _initialOwnerBalance;
}
/*
* @dev Returns the name of the token.
*/
function name() external view returns (string memory _name){
_name = tokenName;
}
/*
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory _symbol){
_symbol = tokenSymbol;
}
/*
* @dev Returns the number of decimals the token uses.
*/
function decimals() external view returns (uint8 _decimals){
_decimals = tokenDecimals;
}
/*
* @dev Returns the total token supply.
*/
function totalSupply()external view returns (uint256 _totalSupply){
_totalSupply = tokenTotalSupply;
}
/*
* @dev Returns the account balance of another account with address _owner.
* @param _owner The address from which the balance will be retrieved.
*/
function balanceOf(address _owner) external view returns (uint256 _balance){
_balance = balances[_owner];
}
/*
* @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
* function SHOULD throw if the "from" account balance does not have enough tokens to spend.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transfer(address payable _to, uint256 _value) public returns (bool _success){
require(balances[msg.sender] >= _value, "Value exceeds balance");
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
/*
* @dev Allows _spender to withdraw from your account multiple times, up to the _value amount. If
* this function is called again it overwrites the current allowance with _value. SHOULD emit the Approval event.
* @param _spender The address of the account able to transfer the tokens.
* @param _value The amount of tokens to be approved for transfer.
*/
function approve(address _spender,uint256 _value) public returns (bool _success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/*
* @dev Returns the amount which _spender is still allowed to withdraw from _owner.
* @param _owner The address of the account owning tokens.
* @param _spender The address of the account able to transfer the tokens.
*/
function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
_remaining = allowed[_owner][_spender];
}
/*
* @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
* Transfer event.
* @param _from The address of the sender.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
require(allowed[_from][msg.sender] >= _value, "Value exceeds allowance");
require(balances[_from] >= _value, "Value exceeds balance");
balances[_from] -= _value;
balances[_to] += _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
}
function transfer(address payable _to, uint256 _value) public returns (bool _success){
require(_value <= balances[msg.sender], "You do not have enough tokens to spend.");
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
_success = true;
}
function approve(address _spender,uint256 _value) public returns (bool _success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
_success = true;
}
function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
require(_value <= balances[_from] , "The address you are sending funds from does not have enough funds.");
require(_value <= allowed[_from][msg.sender], "You are not allowed to spend more than the allowed value.");
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(_from, _to, _value);
_success = true;
}
Hey @Tomahawk
Some funny stuff for your:
pragma solidity 0.6.0;
contract Underflow {
function underflow(uint _x) public view returns (uint) {
return 1-_x;
}
}
Run this one and use the number 2
as parameter to the function underflow
Check the result.
Also I have read from you earlier that in 0.8.0 operators + - : * comes with built-in under/overflow check. So basically you only need + - : * without require or SafeMath.
This is true IF you are using sol 0.8.
You will not always be able to use that solidity version, for example in case you are implementing an oracle contract the has been wrote with an older version of Soldity.
This is why itās important to learn how safemath works.
Happy learning,
Dani
That is good to know, thanks. I didnāt realize you would sometimes actually need an older version
Well okay, I understand SafeMath is needed in pragma < 0.8. However if you use SafeMath in <0.8 or normal operators in >=0.8, what about my second question? Do you really need require? Because you canāt underflow, so you canāt spend more funds than your balance, therefore you shoudnāt need ārequireā statement.
pragma solidity 0.8.0;
/**
* @title ERC20 standard token implementation.
* @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
*/
contract Token {
/*
* Token name.
*/
string internal tokenName;
/*
* Token symbol.
*/
string internal tokenSymbol;
/*
* Number of decimals.
*/
uint8 internal tokenDecimals;
/*
* Total supply of tokens.
*/
uint256 internal tokenTotalSupply;
/*
* Balance information map.
*/
mapping (address => uint256) internal balances;
/*
* Token allowance mapping.
*/
mapping (address => mapping (address => uint256)) internal allowed;
/*
* @dev Trigger when tokens are transferred, including zero value transfers.
*/
event Transfer(address indexed _from,address indexed _to,uint256 _value);
/*
* @dev Trigger on any successful call to approve(address _spender, uint256 _value).
*/
event Approval(address indexed _owner,address indexed _spender,uint256 _value);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
tokenName = _name;
tokenSymbol = _symbol;
tokenDecimals = _decimals;
tokenTotalSupply = _initialOwnerBalance;
balances[msg.sender] = _initialOwnerBalance;
}
/*
* @dev Returns the name of the token.
*/
function name() external view returns (string memory _name){
_name = tokenName;
}
/*
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory _symbol){
_symbol = tokenSymbol;
}
/*
* @dev Returns the number of decimals the token uses.
*/
function decimals() external view returns (uint8 _decimals){
_decimals = tokenDecimals;
}
/*
* @dev Returns the total token supply.
*/
function totalSupply()external view returns (uint256 _totalSupply){
_totalSupply = tokenTotalSupply;
}
/*
* @dev Returns the account balance of another account with address _owner.
* @param _owner The address from which the balance will be retrieved.
*/
function balanceOf(address _owner) external view returns (uint256 _balance){
_balance = balances[_owner];
}
/*
* @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
* function SHOULD throw if the "from" account balance does not have enough tokens to spend.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transfer(address payable _to, uint256 _value) public returns (bool _success){
//checks if the sender of the transfer has enough funds.
require( balances[msg.sender] >= _value, "Not enough funds!");
//make the transaction happen.
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
//emit the event.
emit Transfer(msg.sender, _to, _value);
//return true to end the function.
return true;
}
/*
* @dev Allows _spender to withdraw from your account multiple times, up to the _value amount. If
* this function is called again it overwrites the current allowance with _value. SHOULD emit the Approval event.
* @param _spender The address of the account able to transfer the tokens.
* @param _value The amount of tokens to be approved for transfer.
*/
function approve(address _spender,uint256 _value) public returns (bool _success) {
//set the person that msg.sender wants to approve for this function.
allowed[msg.sender][_spender] = _value;
//emit the event.
emit Approval(msg.sender, _spender, _value);
//return true to end the funciton.
return true;
}
/*
* @dev Returns the amount which _spender is still allowed to withdraw from _owner.
* @param _owner The address of the account owning tokens.
* @param _spender The address of the account able to transfer the tokens.
*/
function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
_remaining = allowed[_owner][_spender];
}
/*
* @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
* Transfer event.
* @param _from The address of the sender.
* @param _to The address of the recipient.
* @param _value The amount of token to be transferred.
*/
function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
//check if there are enough funds in the from balance.
require(_value >= balances[_from], "Not enough funds!");
//check if the caller is allowed to spend the amount
require(_value >= allowed[_from][msg.sender], "Not enough alowed funds!");
//remove the spent amount from the allowance
allowed[_from][msg.sender] = allowed[_from][msg.sender] - _value;
//move the balance from sender to receiver
balances[_from] = balances[_from] - _value;
balances[_to] = balances[_to] + _value;
//emit the transfer
emit Transfer(_from, _to, _value);
//return true to finish the funciton.
return true;
}
}
When i try to deploy this contract i get: creation of Token errored: Error encoding arguments: Error: invalid BigNumber string (argument="value", value="", code=INVALID_ARGUMENT, version=bignumber/5.0.8)
Iāve tried to search it but canāt find any good solution.
Even when I copied the solution from Philip into my remix it still gives this error. I also refreshed the remix site but that also didnāt help.
Hi @Tomahawk
Do you really need require? Because you canāt underflow, so you canāt spend more funds than your balance, therefore you shoudnāt need ārequireā statement.
I highly suggest you the Smart Contract Security course as soon as youāve finished this one.
One of the most famous hacks in Solidity has been made by overflowing a uint.
Because you canāt underflow, so you canāt spend more funds than your balance
This is not the point unfortunately.
It is true that I cannot send more than my balance, however keep in mind that āmy balanceā is saved in a mapping 99.99% of the times.
mapping(address => uint) balances;
This is where SafeMath is fundamental.
If you are using solidity < 0.8 without SafeMath, your contract will surely be hacked
Nice questions anyway, take the Security course I am sure you will love it.
Cheers,
Dani
When i try to deploy this contract i get:
creation of Token errored: Error encoding arguments: Error: invalid BigNumber string (argument="value", value="", code=INVALID_ARGUMENT, version=bignumber/5.0.8)
Can you please post your migration file where this error is generated?
Thanks
Dani
Sorry buuut if you use SafeMath you canāt overflow as well, so I still canāt see why require statement is needed. Hope I will learn that in Security course then.
The issue is not in the transfer function, is in the mappings/ variable that hold the user balance.
Will be all clear once you take the course no worries, we will discuss then if you want
Happy learning and let me know if you need help!
I would love to do that but I canāt find how to upload migration files in this forum.
I was trying something out but now I have deleted all my workspaces i thought it maybe would help(i downloaded them so no worry there) so Iāll first figure out how to set it all back .
I found it! My stupid ass head couldnāt figure out that I needed to put the values first before deploying the contract. Learned something new that you get that error when you donāt put any values in hence the argument="value", value=""
thank you for your time have a nice day !
how does one tweak the test fixture so that the test cases are not dependant on order and that everything is reset between each test case?
doing this but its not right
contract("Dex - Market Orders", (accounts) => {
let dex;
let link;
beforeEach(async function () {
dex = await Dex.deployed();
link = await Link.deployed();
await dex.addToken(web3.utils.fromUtf8("LINK"), link.address, {
from: accounts[0],
});
});
my mistake⦠forgot an await in some of my code
if account[0] wants to BUY Link⦠then why do I need to approve it for link? surely it only applied to spending the link ? @filip
await link.approve(dex.address,50,{from : accounts[0]})