21

I want to create and sign a transaction in browser using web3. In order to create a raw transaction, I think I have to do something like this (hopefully you can fill the gaps of my knowledge)

var pk = '0x6ba33b3f7997c2bf63d82f3baa1a8069014a59fa1f554af3266aa85afee9d0a9';

pk = new Buffer(pk,'hex');

var address = '0xFb4d271F3056aAF8Bcf8aeB00b5cb4B6C02c7368';

var myContractsAddress = '0x0cb4edc28d17c43a75797bf5effc141fd5da8715';

var rawTx = {

nonce: web3.toHex(web3.eth.getTransactionCount(acct1)),

to: myContractsAddress,

gasPrice: web3.toHex(20000000000),

gasLimit: web3.toHex(200000),

value: ***Here I am not sure, am I supposed to encode my list of variables? and do I encode everything to hex, or just integers? And how would that list look like?***

data: *This field is irrelevant for now, and just for documentationpurpose, right?*
}

This should give me my raw transaction (the part about the datafield I unfortunately don't know and would love to get help with!

Lets say my raw transaction is valid and correct at this point, I would have to sign it with the private key now. The solution I've seen used a node.js library called ethereumjs-tx. Is there a solution with web3 only, or do I have to port this library somehow into my browser?

6
  • 1
    ethereumjs-tx doesn't need to be "ported" to the browser, since it already works fine there. Do you have the ABI for your contract? value is the amount of ether you're sending (perhaps 0 if you're just calling a contract function). data is what tells the contract what function you're calling (via a hash called a "function selector") and with what parameters (ABI-encoded). If you have the ABI, web3.js can compute the data field for you.
    – user19510
    Commented Jan 15, 2018 at 17:14
  • Thanks for your reply! I do have the contracts abi. So for value I'd use web3.toHex(0) If you don't mind, please share with me an example of how to fill the data-field. lets assume the abi is stored under var abi = ...;
    – S1r_Mar71n
    Commented Jan 15, 2018 at 19:15
  • Then the two answers here should work for you.
    – user19510
    Commented Jan 15, 2018 at 19:16
  • I've updated my reply again, misstyped it a little
    – S1r_Mar71n
    Commented Jan 15, 2018 at 19:19
  • I think my answer already shows how to fill in the data field.
    – user19510
    Commented Jan 15, 2018 at 19:22

4 Answers 4

19

Below is working code that calls "increment" on https://programtheblockchain.com/dapps/counter. (More info about that sample here: https://programtheblockchain.com/posts/2017/12/13/building-decentralized-apps-with-ethereum-and-javascript/.)

It uses web3.js and ethereumjs-tx:

<!-- from https://github.com/ethereumjs/browser-builds/raw/master/dist/ethereumjs-tx/ethereumjs-tx-1.3.3.min.js -->
<script src="ethereumjs-tx-1.3.3.min.js"></script>
<script>
  var address = "0xf15090c01bec877a122b567e5552504e5fd22b79";
  var abi = [{"constant":true,"inputs":[],"name":"getCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_count","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}];

  var account = "<REDACTED ACCOUNT ADDRESS>";
  var privateKey = "<REDACTED PRIVATE KEY WITHOUT 0x PREFIX>";

  var web3 = new Web3(new Web3.providers.HttpProvider(
    "https://ropsten.infura.io/<REDACTED API KEY"));

  web3.eth.getTransactionCount(account, function (err, nonce) {
    var data = web3.eth.contract(abi).at(address).increment.getData();

    var tx = new ethereumjs.Tx({
      nonce: nonce,
      gasPrice: web3.toHex(web3.toWei('20', 'gwei')),
      gasLimit: 100000,
      to: address,
      value: 0,
      data: data,
    });
    tx.sign(ethereumjs.Buffer.Buffer.from(privateKey, 'hex'));

    var raw = '0x' + tx.serialize().toString('hex');
    web3.eth.sendRawTransaction(raw, function (err, transactionHash) {
      console.log(transactionHash);
    });
  });
</script>
7
  • 2
    okay, I unfortunately do have to bother you again... The increment function doesn't expect any params. What if it would want (string, uint256) i.E. Because I can't just change var data = web3.eth.contract(abi).at(address).increment(string,uint256).getData();
    – S1r_Mar71n
    Commented Jan 16, 2018 at 0:18
  • 1
    The parameters get passed to getData: ...increment.getData("foo", 32);
    – user19510
    Commented Jan 16, 2018 at 0:20
  • Okay, fully figured it out now, you're my hero. Can I thank you somehow?
    – S1r_Mar71n
    Commented Jan 16, 2018 at 0:31
  • 2
    Happy to help! Promote programtheblockchain.com to your Ethereum developer friends. :-)
    – user19510
    Commented Jan 16, 2018 at 0:32
  • I wish I had any developerfriends haha. link me your steamid, I can gift you something there if you want (if you even use steam)
    – S1r_Mar71n
    Commented Jan 16, 2018 at 0:36
18

Using Web3.js 1.0.0

encoded = contractInstance.methods.myMethod(params).encodeABI()

var tx = {
    to : myContractAddress,
    data : encoded
}

web3.eth.accounts.signTransaction(tx, privateKey).then(signed => {
    web3.eth.sendSignedTransaction(signed.rawTransaction).on('receipt', console.log)
});
5
  • How can I access return values from myMethod ?
    – Channa
    Commented Sep 28, 2018 at 6:12
  • Seems like the raw transaction is missing some pieces, not sure if they're necessary -- does this actually work? Does it just infer the nonce, gasPrice, gasLimit, etc?
    – ohsully
    Commented Oct 18, 2018 at 20:30
  • @ohsully Mostly you can infer those params when using web3 and built in accounts/wallet. Occasionally operational params are atypical, which is when you need to manually set them. Commented Nov 27, 2018 at 13:34
  • 3
    In case anyone comes across this, my specific scenario is that I was trying to build a raw transaction and then sign it with an account that isn't stored in web3. If you aren't using web3 to sign, then your tx object also needs to include nonce, gasPrice, gasLimit, value, and chainId. These would normally be inferred by web3, but if you want to keep keys elsewhere and sign using something like eth-lightwallet, that's what your tx object needs to have set.
    – ohsully
    Commented Nov 29, 2018 at 17:12
  • where do you get your privateKey when you only have a keystore file Commented Sep 2, 2019 at 12:44
0

I was lost in this mystery for some time and end up writing a package to solve this. It is web3js-raw which is a simple wrapper around web3.js. Fully functioning sample using this could found here.

You could use browserify to package everything into a single .js file and run on browser. Sample above explains how to use browserify as well.

6
  • Take a look at my answer. I'm curious what's different/better about your library or whether it just does the same sort of thing under the hood as the code in my answer.
    – user19510
    Commented Jan 15, 2018 at 17:51
  • @smarx Surely I'm not doing anything fundamentally different. All I have done is wrapping web3.js in a manner which is easy to use. There are so many moving parts/permutations of use (for instance having variables or not, sending ether with function call or not, involvement of MetaMast under the hood, etc) when you are trying to send a transaction. So my approach is to use sendRawTransaction (which seems to be one of the underlying helper function in web3.js) for all possible cases from deploying a contract to invoke a function.
    – Chim
    Commented Jan 16, 2018 at 2:48
  • Thanks for the explanation! I hope, though, that cases where a user has to turn over their private keys to JavaScript in the browser are rare. :-)
    – user19510
    Commented Jan 16, 2018 at 2:51
  • I could see the convenience of not dealing with private keys for both average users and even for developers. But if DApps ever need to take off, and get into main stream, average users should have easy ways to manage their private keys. Do you think MetaMask would be the best way forward for handling private keys? Personally I feel like it is too much to ask from an average user to install MetaMask. Any thoughts?
    – Chim
    Commented Jan 16, 2018 at 2:57
  • 1
    I think MetaMask (or something like it) is definitely the way to go. If users are pasting their private keys into web pages, I think cryptocurrency will surely die a quick death when everyone gets their money stolen. :-) But it's early days.
    – user19510
    Commented Jan 16, 2018 at 2:59
0

There is a better and simple way to sign and execute the smart contract function. Here your function is addBonus.

First of all we'll create the smart contract instance:

 const createInstance = () => {
  const bscProvider = new Web3(
    new Web3.providers.HttpProvider(config.get('bscRpcURL')),
  );
  const web3BSC = new Web3(bscProvider);
  const transactionContractInstance = new web3BSC.eth.Contract(
    transactionSmartContractABI,
    transactionSmartContractAddress,
  );
  return { web3BSC, transactionContractInstance };
};

Now we'll create a new function to sign and execute out addBonus Function

const updateSmartContract = async (//parameters you need) => {
     try {
    const contractInstance = createInstance();
// need to calculate gas fees for the addBonus
    const gasFees =
      await contractInstance.transactionContractInstance.methods
        .addBonus(
         // all the parameters
        )
        .estimateGas({ from: publicAddress_of_your_desired_wallet });
   const tx = {
      // this is the address responsible for this transaction
      from: chainpalsPlatformAddress,
      // target address, this could be a smart contract address
      to: transactionSmartContractAddress,
      // gas fees for the transaction
      gas: gasFees,
      // this encodes the ABI of the method and the arguments
      data: await contractInstance.transactionContractInstance.methods
        .addBonus(
       // all the parameters
        )
        .encodeABI(),
    };
  // sign the transaction with a private key. It'll return messageHash, v, r, s, rawTransaction, transactionHash
    const signPromise =
       await contractInstance.web3BSC.eth.accounts.signTransaction(
        tx,
        config.get('WALLET_PRIVATE_KEY'),
      );
    // the rawTransaction here is already serialized so you don't need to serialize it again
    // Send the signed txn
    const sendTxn =
      await contractInstance.web3BSC.eth.sendSignedTransaction(
        signPromise.rawTransaction,
      );
    return Promise.resolve(sendTxn);
} catch(error) {
  throw error;
}
    }

Happy coding :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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