Create transaction with Web3.js

web3means
Web3 is too Complicated..

This post is about creating transaction with Web3.js library.

web3.eth.sendTransaction

web3.eth.sendTransaction(transactionObject [, callback])

Sends a transaction to the network.

The from property can also be an address or index from the web3.eth.accounts.wallet. It will then sign locally using the private key of that account, and send the transaction via web3.eth.sendSignedTransaction(). If the properties chain and hardfork or common are not set, Web3 will try to set appropriate values by querying the network for its chainId and networkId.

The Above is description of web3.eth.sendTransaction on official Web3.js document. (web3.eth.sendTransaction)

web3.eth.personal.sendTransaction

web3.eth.personal.sendTransaction(transactionOptions, password [, callback])

This method sends a transaction over the management API.

Sending your account password over an unsecured HTTP RPC connection is highly unsecure.

The Above is description of web3.eth.personal.sendTransaction on official Web3.js document. (web3.eth.personal.sendTransaction)

✔️ my memo

User may use this method like web3.eth.sendTransaction({from: account, ...}). but this method work properly only under condition account is unlocked on node which is connected to. The way of unlock the account explicitly is using web3.eth.personal.unlockAccount. but This way is not recommended. Because Node itself has user’s Private key, unlockAccount has secure Problem. So this method is typically used on some contract testing tool like Ganache. The Below is description of unlockAccount on official Web3.js document. (unlockAccount)

Using this web3.eth.personal.sendTransaction, you are trying to execute the transaction using one of the accounts in the node.

(sendTransaction, signTransaction, sendSigendTransaction differences)

signTransaction

There are 3 methods named as same signTransaction on Web3.js (v1.7.4).

web3.eth.personal.signTransaction (need account unlocked), web3.eth.signTransaction (need account unlocked), web3.eth.accounts.signTransaction

The Below is description of web3.eth.accounts.signTransaction on official Web3.js document. (signTransaction)

web3.eth.accounts.signTransaction(tx, privateKey [, callback]);

Signs an Ethereum transaction with a given private key.

✔️ my memo

Although There are same named 3 methods, It seems web3.eth.accounts.signTransaction is mostly used.

To use EIP-1559, Web3.js version must >= 1.5.0, and type field’s value should be "0x2".

sendSignedTransaction

web3.eth.sendSignedTransaction(signedTransactionData [, callback])

Sends an already signed transaction, generated for example using web3.eth.accounts.signTransaction.

The Above is description of web3.eth.sendSignedTransaction on official Web3.js document. (sendSignedTransaction)

defaultAccount

web3.eth.defaultAccount

This default address is used as the default “from” property, if no “from” property is specified in for the following methods:

  • web3.eth.sendTransaction()
  • web3.eth.call()
  • new web3.eth.Contract() -> myContract.methods.myMethod().call()
  • new web3.eth.Contract() -> myContract.methods.myMethod().send()

Property

String - 20 Bytes: Any ethereum address. You should have the private key for that address in your node or keystore. (Default is undefined)

Example

web3.eth.defaultAccount;
> undefined

// set the default account
web3.eth.defaultAccount = '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe';

✔️ my memo

명세에서, defaultAccount의 설정 간에 “You shold have private key for that address in your node or keystore.” 라는 언급이 되어있고, web3.eth.sendTransaction()을 호출 할 때, “from” 필드가 비워져 있으면 default address로 사용하는 것이 defaultAccount라는 것을 통해 web3.eth.sendTransaction()는 node 또는 keystore에 미리 private key가 주어져 있어야 한다고 간주할 수 있는지?

node 또는 keystore에 미리 private key가 저장되어 있어야 사용할 수 있는, 즉, 보안 문제가 있는 method는 web3.eth.personal.sendTransaction 이며, web3.eth.sendTransaction의 경우, web3.eth.accounts.wallet.add 를 통해 walletlocal에 저장하고 있기 때문에, 상대적으로 보안 문제가 덜 하다.

참고로, web3.eth.getAccounts()를 통해 노드가 컨트롤하는 계좌의 리스트를 반환 받을 수 있다. (Returns a list of accounts the node controls.)

methods.myMethod.encodeABI

myContract.methods.myMethod([param1[, param2[, …]]]).encodeABI()

Encodes the ABI for this method. The resulting hex string is 32-bit function signature hash plus the passed parameters in Solidity tightly packed format. This can be used to send a transaction, call a method, or pass it into another smart contract’s method as arguments. Set the data field on web3.eth.sendTransaction options as the encodeABI() result and it is the same as calling the contract method with contract.myMethod.send().

Some use cases for encodeABI() include: preparing a smart contract transaction for a multisignature wallet, working with offline wallets and cold storage and creating transaction payload for complex smart contract proxy calls.

Example

myContract.methods.myMethod(123).encodeABI();
> '0x58cf5f1000000000000000000000000000000000000000000000000000000000000007B'

✔️ my memo

단순한 이더 전송이 아니라, 컨트랙트의 함수 실행을 위해 data 필드에 어떤 값을 넣어야 할지 methods.myMethod.encodeABI()를 통해 알 수 있다.

Examples

pure ether sending

execute contract function also available by using data field.

const transaction = {
  to: "0x31B98D14007bDEe637298086988A0bBd31184523", // faucet address to return eth
  value: 100,
  gas: 30000,
  maxFeePerGas: 1000000108,
  nonce: nonce,
  // optional data field to send message or execute smart contract ✔️
  // data : compiled solidity source code using https://remix.ethereum.org ✔️
};

const signedTx = await web3.eth.accounts.signTransaction(
  transaction,
  PRIVATE_KEY
);

contract method

const transaction = myContract.methods.myFunc(arg1, arg2, arg3);

const options = {
  to: transaction._parent._address,
  data: transaction.encodeABI(),
  gas: (await web3.eth.getBlock("latest")).gasLimit,
};
const signed = await web3.eth.accounts.signTransaction(options, PRIVATE_KEY);

contract method full example

var Web3 = require("web3");

const rpcURL = process.env.RPC_URL;
const web3 = new Web3(rpcURL);

async function send(web3, transaction) {
  while (true) {
    try {
      const options = {
        to: transaction._parent._address,
        data: transaction.encodeABI(),
        gas: (await web3.eth.getBlock("latest")).gasLimit,
      };
      const signed = await web3.eth.accounts.signTransaction(
        options,
        PRIVATE_KEY
      );
      const transactionReceipt = await web3.eth.sendSignedTransaction(
        signed.rawTransaction
      );
      return transactionReceipt;
    } catch (error) {
      console.log(error.message);
      console.log("Press enter to try again...");
      await new Promise(function (resolve, reject) {
        process.stdin.resume();
        process.stdin.once("data", function (data) {
          process.stdin.pause();
          resolve();
        });
      });
    }
  }
}

const receipt = await send(web3, myContract.methods.myFunc(arg1, arg2, arg3));

Fact Check

sendTransaction - able both on just Ether Sending and contract call

sendTransaction vs. signTransaction & sendSignedTransaction - Internally, sendTransaction signs the transaction and maybe is the most used. Signing and sending the transaction if different steps is more flexible. User can use an account to sign the transaction and another to send and pay for the fees etc.

web.eth.sendTransaction vs. web3.eth.personal.sendTransaction - The Former works on local computer memory. but The Latter one works over the network on node through JSONRPC call. so The Latter one needs wallet’s private key or password in plain text which could be occur security issue.

References

difference between sendTransaction & sendSignedTransaction

Sending Transaction on EIP-1559

difference betweeon web3.eth.personal & web3.eth.accounts