14

I created some custom token in Ropsten testnet using this guide: https://steemit.com/ethereum/@maxnachamkin/how-to-create-your-own-ethereum-token-in-an-hour-erc20-verified

I can send it to other accounts using MetaMask, but can't figure out how to do it in node.js using web3, ethereumjs-tx and Web3 JavaScript app API.

My code at the moment looks like this:

var count = web3.eth.getTransactionCount("0x26...");
var abiArray = JSON.parse(fs.readFileSync('mycoin.json', 'utf-8'));
var contractAddress = "0x8...";
var contract = web3.eth.contract(abiArray).at(contractAddress);
var rawTransaction = {
    "from": "0x26...",
    "nonce": web3.toHex(count),
    "gasPrice": "0x04e3b29200",
    "gasLimit": "0x7458",
    "to": contractAddress,
    "value": "0x0",
    "data": contract.transfer("0xCb...", 10, {from: "0x26..."}),
    "chainId": 0x03
};

var privKey = new Buffer('fc3...', 'hex');
var tx = new Tx(rawTransaction);

tx.sign(privKey);
var serializedTx = tx.serialize();

web3.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function(err, hash) {
    if (!err)
        console.log(hash);
    else
        console.log(err);
});

In this case code just stops at contract.transfer("0xCb...", 10, {from: "0x26..."}) part and request is just pending. Couldn't find any guide for doing similar things. Found some code here:

Web3 sending custom tokens by using the transfer function. Need to set the from account

And here:

How to transfer ERC20 tokens using web3js

But still got stucked, don't know what I'm missing.

  • Well looking at the web3 documentation you can try contract.methods. transfer("0xCb...", 10).send({from : XX},function(){dostuff})instead of sending sendRawTransaction. – Adrien Forbu Aug 21 '17 at 14:54
  • You should use getData to generate the raw transaction data, like this "data": contract.transfer.getData("0xCb...", 10, {from: "0x26..."}),. – Ismael Aug 21 '17 at 23:51
  • 1
    This answer explain how to use getData ethereum.stackexchange.com/a/12932 – Ismael Aug 21 '17 at 23:53
  • @TomasNavickas were you using web3.js v0.20.x? Can you kindly post a complete example as the answer and marked as solved. Thank you. – Trav L Aug 31 '17 at 14:11
  • @TomasNavickas This works, but it's sending wei (ether) instead of the actual token. Any idea why? – Viper Oct 29 '17 at 4:33
14

I'm using web3.js version: 0.20.1 in node.js express application. I'm running Parity in Virtualbox Ubuntu machine.

The correct code looks like following:

var count = web3.eth.getTransactionCount("0x26...");
var abiArray = JSON.parse(fs.readFileSync('mycoin.json', 'utf-8'));
var contractAddress = "0x8...";
var contract = web3.eth.contract(abiArray).at(contractAddress);
var rawTransaction = {
    "from": "0x26...",
    "nonce": web3.toHex(count),
    "gasPrice": "0x04e3b29200",
    "gasLimit": "0x7458",
    "to": contractAddress,
    "value": "0x0",
    "data": contract.transfer.getData("0xCb...", 10, {from: "0x26..."}),
    "chainId": 0x03
};

var privKey = new Buffer('fc3...', 'hex');
var tx = new Tx(rawTransaction);

tx.sign(privKey);
var serializedTx = tx.serialize();

web3.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function(err, hash) {
    if (!err)
        console.log(hash);
    else
        console.log(err);
});
  • Can u please share the tutorial of using web3.js for ether and token transfers – veeresh kumbar Apr 12 '18 at 7:29
  • What does the Buffer do in this case? Why is the var privKey not just assigned as a string containing the private key? – John Murphy Jul 20 '18 at 15:01
9

I found many answers which were out of date or were missing important information. Here's what finally worked for me in my node.js project using web3 version 1.0.0-beta.26. Note that this is for Ethereum Main Net. To use the Robsten test net, change the chainId to 0x03

// Get private stuff from my .env file
import {my_privkey, infura_api_key} from '../.env'

// Need access to my path and file system
import path from 'path'
var fs = require('fs');

// Ethereum javascript libraries needed
import Web3 from 'Web3'
var Tx = require('ethereumjs-tx');

// Rather than using a local copy of geth, interact with the ethereum blockchain via infura.io
const web3 = new Web3(Web3.givenProvider || `https://mainnet.infura.io/` + infura_api_key)

// Create an async function so I can use the "await" keyword to wait for things to finish
const main = async () => {
  // This code was written and tested using web3 version 1.0.0-beta.26
  console.log(`web3 version: ${web3.version}`)

  // Who holds the token now?
  var myAddress = "0x97...";

  // Who are we trying to send this token to?
  var destAddress = "0x4f...";

  // If your token is divisible to 8 decimal places, 42 = 0.00000042 of your token
  var transferAmount = 1;

  // Determine the nonce
  var count = await web3.eth.getTransactionCount(myAddress);
  console.log(`num transactions so far: ${count}`);

  // This file is just JSON stolen from the contract page on etherscan.io under "Contract ABI"
  var abiArray = JSON.parse(fs.readFileSync(path.resolve(__dirname, './tt3.json'), 'utf-8'));

  // This is the address of the contract which created the ERC20 token
  var contractAddress = "0xe6...";
  var contract = new web3.eth.Contract(abiArray, contractAddress, { from: myAddress });

  // How many tokens do I have before sending?
  var balance = await contract.methods.balanceOf(myAddress).call();
  console.log(`Balance before send: ${balance}`);

  // I chose gas price and gas limit based on what ethereum wallet was recommending for a similar transaction. You may need to change the gas price!
  var rawTransaction = {
      "from": myAddress,
      "nonce": "0x" + count.toString(16),
      "gasPrice": "0x003B9ACA00",
      "gasLimit": "0x250CA",
      "to": contractAddress,
      "value": "0x0",
      "data": contract.methods.transfer(destAddress, transferAmount).encodeABI(),
      "chainId": 0x01
  };

  // Example private key (do not use): 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109'
  // The private key must be for myAddress
  var privKey = new Buffer(my_privkey, 'hex');
  var tx = new Tx(rawTransaction);
  tx.sign(privKey);
  var serializedTx = tx.serialize();

  // Comment out these three lines if you don't really want to send the TX right now
  console.log(`Attempting to send signed tx:  ${serializedTx.toString('hex')}`);
  var receipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'));
  console.log(`Receipt info:  ${JSON.stringify(receipt, null, '\t')}`);

  // The balance may not be updated yet, but let's check
  balance = await contract.methods.balanceOf(myAddress).call();
  console.log(`Balance after send: ${balance}`);
}

main();

Note that sometimes the send will "fail" because the tx wasn't mined within 50 blocks. In my copy of web3 source code, I changed TIMEOUTBLOCK from 50 to 500 so I wouldn't have to deal with that.

Note that this code does not have error handling - you may want to add some.

  • So one would need to get hold and manage the abi json for every singe ERC-20 that needs to be handled? – djskinner Dec 13 '17 at 16:32
  • Or is it possible to use a 'standard ERC20 token ABI' as this module suggests is possible? github.com/danfinlay/human-standard-token-abi – djskinner Dec 13 '17 at 16:44
  • To call a function on any contract, all you need is the contract's address and the signature of the function. ERC20 defines several functions. As long as the contract at the address defines a public function with the same signature as the one you're calling, you can call it. – destenson Dec 13 '17 at 19:05
  • I'm getting this error: Error: Returned error: invalid sender – Nishchit Dhanani Dec 24 '17 at 14:39
  • 1
    @zero_cool no, it has to be larger than the previous nonce. That's why we call getTransactionCount. – Paul Berg Jun 1 '18 at 9:33
1

If we just want send a transaction with erc20 method transfer,we can build a Contract Object by using minABI and the contract address,such as the follow code:

let minABI = [
// transfer
{
    "constant": false,
    "inputs": [
        {
            "name": "_to",
            "type": "address"
        },
        {
            "name": "_value",
            "type": "uint256"
        }
    ],
    "name": "transfer",
    "outputs": [
        {
            "name": "success",
            "type": "bool"
        }
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
}
];
let contractAddres="put the erc20 contract address here";

let contract = await new web3.eth.Contract(minABI, contractAddr);

contract.methods.transfer("your account", "amount of erc20 tokens you want transfer").send({
        from: "your account"
    });

and this is work for me, hope this will help. :D

1

Came across this documentation in one of the projects I'm using in my dapp: https://developers.fortmatic.com/docs/smart-contract-functions

The documentation explains pretty thoroughly what to do step-by-step to send ERC20 token transfers for both pre-1.0 (0.20.x) and post-1.0 web3 versions.

Here's a preview of what the 0.20.x version of web3 code looks like.

// Initialize provider
import Fortmatic from 'fortmatic';
import Web3 from 'web3';

const fm = new Fortmatic('YOUR_API_KEY');
window.web3 = new Web3(fm.getProvider()); // Can replace with MetaMask web3 provider

// Get the contract ABI from compiled smart contract json
const erc20TokenContractAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"tokens","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokens","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdrawEther","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"_totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenOwner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeSub","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"tokens","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeDiv","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"tokens","type":"uint256"},{"name":"data","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeMul","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"tokens","type":"uint256"}],"name":"transferAnyERC20Token","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenOwner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeAdd","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tokenOwner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"Approval","type":"event"}];

// Create contract object
const tokenContract = web3.eth.contract(erc20TokenContractAbi);
// Instantiate contract
const tokenContractInstance = tokenContract.at('0x8EBC7785b83506AaA295Bd9174e6A7Ad5681fb80');

const toAddress = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D';
// Calculate contract compatible value for transfer with proper decimal points using BigNumber
const tokenDecimals = web3.toBigNumber(18);
const tokenAmountToTransfer = web3.toBigNumber(100);
const calculatedTransferValue = web3.toHex(tokenAmountToTransfer.mul(web3.toBigNumber(10).pow(tokenDecimals)));

// Call contract function (non-state altering) to get total token supply
tokenContractInstance.totalSupply.call(function(error, result) {
  if (error) throw error;
  console.log(result);
});

// Get user account wallet address first
web3.eth.getAccounts(function(error, accounts) {
  if (error) throw error;
  // Send ERC20 transaction with web3
  tokenContractInstance.transfer.sendTransaction(toAddress, calculatedTransferValue, {from: accounts[0]}, function(error, txnHash) {
    if (error) throw error;
    console.log(txnHash);
  });
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.