Detailed explanation of constructing Ethereum transaction object in Js

       This article uses the etherslibrary as the underlying implementation, and describes the detailed properties of the Ethereum transaction object built in Javascript. This article assumes that the reader has a certain basic knowledge of Ethereum.

1. What is a etherslibrary

       The following is an original introduction of its document:

       The ethers.js library aims to be a complete and compact library for interacting with the Ethereum Blockchain and its ecosystem. It was originally designed for use with ethers.io and has since expanded into a much more general-purpose library.

       The rough meaning is ethers.jsa complete and compact library applied to Ethereum and its ecosystem. It was originally designed to be used on ethers.io and slowly expanded into a multifunctional library.

       ethersThe library has the following characteristics:

  • The private key is stored on the client, safe and risk-free
  • Support import and export wallets in json format (can be used for Geth or Parity)
  • Support import and export mnemonic words and hardware wallet, mnemonic words support multiple languages
  • Support multiple ABI formats, including ABIv2 and human-readable ABI
  • Support multiple ways to connect to Ethereum nodes, such as JSON-RPC, INFURA, Etherscan or MetaMask.
  • ENS names are fully supported as the first type of element
  • The library is very small (284kb uncompressed, 88kb compressed)
  • Complete functions to meet all your needs for Ethereum
  • Detailed documentation
  • Added a lot of test cases
  • TypeScript readable
  • MIT certificate (including its dependencies), completely open source

Second, build a trading object

       In ethers.js, an Ethereum transaction object is an ordinary object {}, which contains the following optional attributes:

  • to
  • gasLimit
  • gasPrice
  • nonce
  • data
  • value
  • chainId

       The above attributes are all optional, which means they can be omitted, but not all of them, there must be at least one attribute. We create a transaction object in the following way:

// All properties are optional
let transaction = {
    
    
    nonce: 0,
    gasLimit: 21000,
    gasPrice: utils.bigNumberify("20000000000"),

    to: "0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290",
    // ... or supports ENS names
    // to: "ricmoo.firefly.eth",

    value: utils.parseEther("1.0"),
    data: "0x",

    // This ensures the transaction cannot be replayed on different networks
    chainId: ethers.utils.getNetwork('homestead').chainId
}

       Below we explain these attributes through actual transactions on the Kovan testnet.

3. Detailed explanation of transaction object attributes

       There are the following code snippets, we will modify or add to this snippet in future transactions. This is a transaction that creates a contract:

let data='0x60....'
let provider = ethers.getDefaultProvider('kovan')
let wallet_new = wallet.connect(provider)
let trans = {
    
    
    data:inputData
}
wallet_new.sendTransaction(trans).then( tx => {
    
    
    console.log(tx)
}).catch( err => {
    
    
    console.log(err)
})

As you can see, our transaction object has only one attribute data, and its value is the bytecode for creating the contract. Note: The bytecode when creating the contract is not the bytecode compiled by the creating contract, but the bytecode that can be obtained by running the bytecode of the created contract.

       Let's take a look at the printed transaction response (Transaction Response):
Insert picture description here
       you can see that in the transaction response object, in addition to toattributes, other attributes exist. So the attribute mentioned above can be omitted means that it can be omitted when constructing the transaction object. If omitted, the underlying etherslibrary will automatically set it up for you. Let's start with this simplest transaction object, increase and explain its properties step by step.

3.1 to

       Since the toattribute is null, we will start with the toattribute. toRepresents the address of the callee in the transaction.

       A transaction on Ethereum must have an initiator (external account, non-contract account), usually from. Because the etherslibrary we use uses the wallet to sign transactions, so whoever signs is whoever signs from. The transaction usually has a receiver (both external account and contract account), that is to. Why is it usual? Because like our previous example, there is no receiver when the contract is created. Although the address of the contract will be toreturned as an attribute after the contract is created , this toaddress is empty when it is created . Let's take a look at the screenshot on etherscan to deepen this impression:
Insert picture description here
       you can see that after the transaction is executed, this toattribute is the address of the new contract. Let me add that the address of the contract is calculated based on the address of the caller and the number of transactions (nonce) that the caller has completed. Therefore, before a contract is actually deployed, the address is set and can be obtained.

       To summarize, the toattribute is the address of the callee in the transaction. Specifically: if you are transferring ETH to an external account, it is the ETH receiving address; if you are calling a contract (transferring ETH to a contract account is also a calling contract), it is the contract address; if you are creating a contract, because there is no callee at this time, Just default it.

3.2 data

       Next we talk about the above code uses the property: data. During the transaction, we can send transaction data along with the transaction. Transaction data can be method calls to the contract, or some meaningless data, which is sometimes called payload. In the above example, datathe value of the attribute is the bytecode of the contract we created. Let us add an toattribute to the above transaction object and modify datathe value of the attribute:

let trans = {
    
    
    data:"0x496c6f7665457468657265756d",
    to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}

       Here tois an external account address, which datais the string of "I love Ethereum" converted into hexadecimal value ( datamust 0xstart with). The response after the transaction is sent is as follows:
Insert picture description here
       Let's look at the result on etherscan:
Insert picture description here
       From the code snippet, we can see that we directly sent a message (string) to an account. At the bottom of InputData, it displays native data by default. Select View Input As UTF-8, and IloveEthereum will be displayed. No spaces are shown here because the tool I used did not encode spaces. Does this function of sending a string to an account look like sending a short message to a mobile phone number? You can even send an article (but with a lot of fees), is Ethereum interesting?

       If the data sent is the data when the contract method is called, it usually has a fixed format and cannot be arbitrary data. Examples are as follows:

data:0x07391dd6000000000000000000000000000000000000000000000000000000000000000a

       Here, the first 8 bits 07391dd6of the first 32 bytes are function selectors, and after 32 bytes are the corresponding types of data. Interested readers can read related articles on Ethereum coding for themselves.

       Well, to summarize: the dataattribute is the data sent with the call. If the called object is a contract, it is usually the code of the contract calling method; if it is to create a contract, it is the created bytecode; if the called object is an external account, the content of this data is arbitrary (the external account has no code , And will not execute the sent data).

3.3 value

       valueThe attribute represents the amount of Ether sent with this transaction. Regardless of whether the transaction type is direct ETH transfer (including transfer to the contract and transfer to an external account), creation of a contract (in this case ETH will be used as the initial ETH of the created contract), or contract call (the contract method is payable), it is faithfully recorded The amount of ETH you sent in the transaction (not including the handling fee, the handling fee is an additional consumption). Let's just add a transaction object valueproperty, its value is attention weiunits. And usually when we are in a general reference to the Ethernet currency etherunits, need to make use of a conversion.

let trans = {
    
    
    data:"0x496c6f7665457468657265756d",
    value:ethers.utils.parseEther('0.1'),
    to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}

valueThe value in the code is 0.1 ETH. Let us send this transaction:
Insert picture description here
       In JS, if the number is relatively large, it will exceed the upper limit of js decimal representation (about 10 ** 15), so BigNumber is generally used to interact with Ethereum. You can see that the number of WEI sent is converted into a BigNumber. Let's look at the result of etherscan again:
Insert picture description here
       it is not shown here databecause I did not click Click to see More to expand. As you can see, we did send 0.1 ETH with the transaction.

3.4 gasLimitandgasPrice

       Next we will introduce two attributes related to gas: gasLimitand gasPrice. This gasLimitrefers to the maximum gas consumption of this transaction, and gasPricerefers to the price you are willing to pay for the actual gas consumed. The specific amount of gas consumed is multiplied by gasPricethe fee you are willing to pay to the miner. After the transaction is executed, the unconsumed gas will be returned to you (the transaction error situation will not be discussed here, in this case sometimes the unconsumed gas will not be refunded).

       gasLimitUsually used to limit a certain transaction can not consume too many resources. Here is a usage scenario: We often use MetaMask to transfer funds directly to external accounts. The gasLimitdefault value in MetaMask is 23000, and the minimum cannot be lower than 21000.
Insert picture description here

       Let us look at a specific transfer transaction on etherscan:
Insert picture description here
       As you can see from the above figure, when we do not send any data with the transaction (the dataattribute is empty, if it is not empty, additional gas will be consumed), to an external Account transfer will consume 21,000 gas, which is basically fixed. Therefore, the gasLimitupper limit of this transaction is also set 21000, the utilization rate 100%.

       Ours in the picture above gasPriceis 5 Gwei. The higher the price you give, the faster the transaction, and of course the higher your handling fee. Usually gasPrice, when we talk about it, we all use it Gweias a unit, but we still have to convert it when we use it wei. This 5 Gweimultiplied by the consumed gas 21000is exactly what is shown in the figure above Transaction Fee: 0.000105ETH. When the author writes here, the price of ETH is around $205, so the handling fee for sending it is about 0.15RMB.

       Let us add these two attributes to the transaction object to see if the excess gas is consumed. Our gasLimit is set to 100000, gasPriceset to 3 Gwei, let us resend the transaction:

let trans = {
    
    
    data:"0x496c6f7665457468657265756d",
    value:ethers.utils.parseEther('0.1'),
    gasLimit:100000,
    gasPrice:ethers.utils.parseUnits("3",'gwei'),
    to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}

       Here, because ours gasLimitcannot exceed the decimal limit of JS, the decimal system is used directly 100000. The transaction response is:
Insert picture description here
       we directly look at the transaction result on etherscan:
Insert picture description here
       because we sent I love Ethereumthis string with the transaction , we consumed 208 more gas. According to the fee we deduct, the unused gas is not included in the cost.

       For gasLimitspeaking generally in use ethersdo not need to set the library, let it default on the line. If you want to set it manually, you can use it to estimate first, and then expand it upwards appropriately, such as the following code snippet:

let args = [_address,amount]
let gasLimit = await contract.estimate.transfer(...args)
let step = ethers.utils.bigNumberify(1000)
gasLimit = gasLimit.add(step)

       For gasPricespeaking, generally set to 5 Gweior under normal circumstances 6 Gwei. It can be set to 1.5 Gweior when the gas consumes a lot or the network is idle 2 Gwei. However, the transaction time will be extended in this way, and it may even fail. If you want to trade quickly, set it to 10 Gweior 20 Gweieven higher, but this will cost more. If you have more money, you will be fast, and if you have less money, you will be slow. The reason is that simple. And be careful: too low a fee may cause no miners to pack the transaction, and the transaction will fail. Of course, if you are on the testnet, you can set it higher, because you don't have to really spend money.

3.5 nonce

       In the transaction object, it noncerepresents the number of transactions completed by the address. It starts from 0 and is an automatically increasing integer. Usually, we don't need to set it. But in some special cases, it can be set manually. One scenario is when covering transactions. You can manually set the nonce to the nonce value of a transaction that has been sent but not yet completed to overwrite the transaction. Usually the purpose of this is to speed up the transaction (increase gasPrice) or completely use a new transaction. This is also easy to understand. For example, my transaction No. 122 is to send an ETH to A, but before this transaction is not sent or completed, I made an urgent modification to change this transaction No. 122 to send an ETH to B. At this point, I only need to set the nonce of the new transaction to 122.

       If you want to set in the transaction object that you usually use, you need to query the number of transactions you have completed. This number is the nonce value you should use. Use the following code:

let address = "0x02F024e0882B310c6734703AB9066EdD3a10C6e0";

provider.getTransactionCount(address).then((transactionCount) => {
    
    
    console.log("Total Transactions Ever Sent: " + transactionCount);
});

       It is worth noting that: nonce has a special usage, you can specify a future value. For example, if the number of transactions you have currently completed is 2096, then the nonce value for the next transaction should be 2097. At this point, you can also skip 2097 and set it to 2098, so what will happen? At this time, the transaction numbered 2098 is equivalent to a delayed transaction, which will be sent out, but will not be executed, and you will not be able to query it on etherscan. Then we perform a normal transaction with the nonce value set to 2097, and the transaction will be sent and executed. Here comes the important point: in the next block, the transaction with a nonce of 2098 will also be executed (because 2097 has already been executed, it's its turn).

3.6 chainId

       chainIdRepresents the network ID you want to initiate a transaction. With three main network and test network, for example, the main network (mainnet, but in ethersthe still call homesteadhome) is 1, Ropstenthe test network 3, Rinkebytest network 4, Kovanthe test network to 42. Custom network can be set by yourself, etc.

       Generally speaking, there is no need to set this when using the wallet chainId. Because the wallet login will bind a network, it is the network of your transaction object. But you can also manually set to a specific value to prevent transactions on the wrong network. You can directly use the decimal number value above, or you can use ethersthe sample code in:

chainId: ethers.utils.getNetwork('homestead').chainId

       If we are trading on the Kovan testnet, the parameters in the method must be changed kovan. Let us add chainIdand nonceto the transaction. And change the value to 0.01ETH to distinguish.

let count = await provider.getTransactionCount(wallet_new.address)
let trans = {
    
    
    data:"0x496c6f7665457468657265756d",
    value:ethers.utils.parseEther('0.01'),
    gasLimit:100000,
    nonce: count,
    chainId:ethers.utils.getNetwork('kovan').chainId,
    gasPrice:ethers.utils.parseUnits("3",'gwei'),
    to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}

Here is the transaction response:
Insert picture description here
       Because the numbers 2097 and 2098 nonceare consumed when using future values, the number is now 2099. Let's take a look at the results on etherscan:
Insert picture description here
       you can see that the amount of ETH sent is 0.01 ETH, and the nonce is 2099. Someone may ask why it is not displayed on etherscan chainId, because etherscan is divided into several sites based on the mainnet and testnet, and each site only displays transactions on its own network. For example, the actual URL of etherscan I visited is:

https://kovan.etherscan.io/tx/0x4db8e6b4096d6c27be341b73af99a8d0477e19ba483248c1fdb6fb431fbb3646

       All transactions shown on this site chainIdare 42.

Four, summary

       In this article, we give a detailed introduction to the specific properties of the manually created Ethereum transaction object. These attributes can be omitted, but they cannot be omitted (because it is meaningless to omit them all). The most commonly used ones are to, valueand dataattributes. Note: This is only an attribute that needs to be set when manually creating a transaction object in the code; if you directly use a common wallet (such as MetaMask or Trust wallet), the wallet will have a UI interface to help you set everything up. However, it is necessary to clarify the basis of the implementation. I hope this article can provide a little help to developers on Ethereum.

You are welcome to leave a message to point out errors or suggest improvements.

Guess you like

Origin blog.csdn.net/weixin_39430411/article/details/104188489