I suspect that your issue is because you have not approved the transfer required to call transferFrom(...)
.
Here are the relevant functions:
/* Send coins */
function transfer(address _to, uint256 _value) {
if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough
if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place
}
/* Allow another contract to spend some tokens in your behalf */
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
returns (bool success) {
allowance[msg.sender][_spender] = _value;
tokenRecipient spender = tokenRecipient(_spender);
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
/* A contract attempts to get the coins */
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
if (balanceOf[_from] < _value) throw; // Check if the sender has enough
if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
if (_value > allowance[_from][msg.sender]) throw; // Check allowance
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
allowance[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
return true;
}
If you are the current owner of the tokens, you can call transfer(...)
to transfer your balance to another address.
However if you want to transfer from another address, you will need to call transferFrom(...)
which will check the allowance[...][...]
data structure to confirm whether this transfer has been approved.
So, call approveAndCall(...)
first to approve your transfer and you should be able to call transferFrom(...)
to transfer your tokens.
When you call transferFrom(...)
without first calling approveAndCall(...)
, and error is thrown in the line
if (_value > allowance[_from][msg.sender]) throw; // Check allowance
This error will appear as an Out Of Gas error in the Ethereum Wallet.
If you are comfortable using geth
, see How can the transaction status from a thrown error be detected when gas can be exactly the same as the gasUsed for a successful transaction? for the following code that can be used to check the exact reason for your error:
> var status = debug.traceTransaction("0xd23219e2ea10528b245deb9e47993cae2ffd3ffe9fc27aeb808e94fc4e75d37b")
undefined
> if (status.structLogs.length > 0) {
console.log(status.structLogs[status.structLogs.length-1].error)
}
"invalid jump destination (PUSH1) 2"
The documentation is skimpy on details and it took me forever to figure out the correct way with the help of your links, anyways, here is what works for me:
Tested on Geth : 1.6.7 -stable , web3.js :0.20.2 Parity/v1.7.2 & Kovan testnet.
I'll be using the minimal token as example :
pragma solidity ^0.4.0;
contract minimalToken {
mapping (address => uint256) public balanceOf;
function minimalToken(uint256 initialSupply) {
balanceOf[msg.sender] = initialSupply;
}
function transfer(address _to, uint256 _value) {
require(balanceOf[msg.sender] >= _value);
require(balanceOf[_to] + _value >= balanceOf[_to]);
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
}
}
I'll be focusing on the transfer method which takes 2 arguments,( address, amount) to transfer:
var contract = web3.eth.contract(contractABI).at("0x8caaa1f263ff14d0276ff1a1a6ed15c51159d6e0");
var receiverAddress = '0x00Ce6C92856A657979E7728005DBc9acD002Eb09';
var callData = contract.transfer.getData(receiverAddress, 2000);
var gasEstimate = web3.eth.estimateGas({
from: web3.eth.coinbase,
to: "0x8caaa1f263ff14d0276ff1a1a6ed15c51159d6e0",
data: callData
});
var gasPrice = web3.eth.gasPrice;
console.log('gas Price: ' + gasPrice);
console.log('Estimated Transaction gas: ' + gasEstimate);
// gas Price: 21000000000
// Estimated Transaction gas: 34207
One thing that Threw me off, was that I kept on getting transaction errors due to me not including the from: field on the transaction.
Hope this helps.
Best Answer
As the body of your function stands, I do not see how a different value of
msg.value
will change the gas used.Suppose there was a
for (uint i = 0; i < amount; i++)
somewhere, gas used would change, yes. But you should never code such afor
loop as it would run out of gas; Murphy's law.Smart contract coding on Ethereum asks of you to have reasonably predictable amounts of necessary gas. Your function is properly written, be happy.