Get Started with web3 Development Easily Based on Ethereum Using FMZ (12)

in #fmz6 months ago

Unit conversions

Many of the calculations related to Ethereum have values that exceed the maximum safe integer of the JavaScript language. Therefore, some methods are needed on the FMZ Quant Trading Platform to handle large values, which we have used specifically in previous courses and have not covered in detail. This section will discuss this aspect in detail.

Print the maximum safe integer defined in the JavaScript language:

function main() {
    Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER)
}

Running results:

Number.MAX_SAFE_INTEGER: 9007199254740991

BigInt

The smallest unit defined in Ethereum is 1wei, and the definition 1Gwei is equal to 1000000000 wei. 1Gwei is not really a very large number in Ethereum-related calculations, and some data is much larger than it. So these data with very large values can easily exceed Number.MAX_SAFE_INTEGER: 9007199254740991.

At FMZ Quant Trading Platform, we use the platform's BigInt object to represent these very large integer data. Use the constructor BigInt() to construct the BigInt object. You can construct BigInt objects using numeric, hexadecimal numeric strings as parameters. Use the toString() method of BigInt object to output the data represented by the object as a string.

The operations supported by the BigInt object are:

  • Addition: +
  • Subtraction: -
  • Multiplication: *
  • Division: /
  • Modulo operations: %
  • Power operations: *

Refer to the following code examples:

function main() {
    // Decimal representation of 1Gwei
    var oneGwei = 1000000000

    // Decimal to hexadecimal conversion of 1Gwei
    var oneGweiForHex = "0x" + oneGwei.toString(16)

    Log("oneGwei : ", oneGwei)
    Log("oneGweiForHex : ", oneGweiForHex)

    // Constructing BigInt objects
    Log("1Gwei / 1Gwei : ", (BigInt(oneGwei) / BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei * 1Gwei : ", (BigInt(oneGwei) * BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei - 1Gwei : ", (BigInt(oneGwei) - BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei + 1Gwei : ", (BigInt(oneGwei) + BigInt(oneGweiForHex)).toString(10))
    Log("(1Gwei + 1) % 1Gwei : ", (BigInt(oneGwei + 1) % BigInt(oneGweiForHex)).toString(10))
    Log("1Gwei ** 2 : ", (BigInt(oneGwei) ** BigInt(2)).toString(10))
    Log("The square root of 100 : ", (BigInt(100) ** BigFloat(0.5)).toString(10))

    Log("Number.MAX_SAFE_INTEGER : ", BigInt(Number.MAX_SAFE_INTEGER).toString(10))
    Log("Number.MAX_SAFE_INTEGER * 2 : ", (BigInt(Number.MAX_SAFE_INTEGER) * BigInt("2")).toString(10))
}

Debugging tool testing:

2023-06-08 11:39:50     Info    Number.MAX_SAFE_INTEGER * 2 : 18014398509481982
2023-06-08 11:39:50     Info    Number.MAX_SAFE_INTEGER : 9007199254740991
2023-06-08 11:39:50     Info    The square root of 100 : 10
2023-06-08 11:39:50     Info    1Gwei ** 2 : 1000000000000000000
2023-06-08 11:39:50     Info    (1Gwei + 1) % 1Gwei : 1
2023-06-08 11:39:50     Info    1Gwei + 1Gwei : 2000000000
2023-06-08 11:39:50     Info    1Gwei - 1Gwei : 0
2023-06-08 11:39:50     Info    1Gwei * 1Gwei : 1000000000000000000
2023-06-08 11:39:50     Info    1Gwei / 1Gwei : 1
2023-06-08 11:39:50     Info    oneGweiForHex : 0x3b9aca00
2023-06-08 11:39:50     Info    oneGwei : 1000000000

BigFloat

The BigFloat object is used similarly to the BigInt object to represent floating point numbers with larger values, and it also supports addition, subtraction, multiplication and division.
The BigFloat object supports the toFixed() method.

Refer to the following code example:

function main() {
    var pi = 3.14
    var oneGwei = "1000000000"
    var oneGweiForHex = "0x3b9aca00"

    Log("pi + oneGwei : ", (BigFloat(pi) + BigFloat(oneGwei)).toFixed(2))
    Log("pi - oneGweiForHex : ", (BigFloat(pi) - BigFloat(oneGweiForHex)).toFixed(2))
    Log("pi * 2.0 : ", (BigFloat(pi) * BigFloat(2.0)).toFixed(2))
    Log("pi / 2.0 : ", (BigFloat(pi) / BigFloat(2.0)).toFixed(2))
}

Debugging tool testing:

2023-06-08 13:56:44     Info    pi / 2.0 : 1.57
2023-06-08 13:56:44     Info    pi * 2.0 : 6.28
2023-06-08 13:56:44     Info    pi - oneGweiForHex : -999999996.86
2023-06-08 13:56:44     Info    pi + oneGwei : 1000000003.14

BigDecimal

The BigDecimal object is compatible with integer values and floating point values, and supports initialization with the BigInt object and the BigFloat object, and it also supports addition, subtraction, multiplication and division.

Refer to the following code example:

function main() {
    var pi = 3.1415
    var oneGwei = 1000000000
    var oneGweiForHex = "0x3b9aca00"

    Log("pi : ", BigDecimal(pi).toFixed(2))
    Log("oneGwei : ", BigDecimal(oneGwei).toString())
    Log("oneGweiForHex : ", BigDecimal(BigInt(oneGweiForHex)).toString())

    Log("BigInt(oneGwei) : ", BigDecimal(BigInt(oneGwei)).toString())    
    Log("BigFloat(pi) : ", BigDecimal(BigFloat(pi)).toFixed(4))

    Log("oneGwei + pi : ", (BigDecimal(oneGwei) + BigDecimal(pi)).toString())
    Log("oneGwei - pi : ", (BigDecimal(oneGwei) - BigDecimal(pi)).toString())
    Log("2.0 * pi : ", (BigDecimal(2.0) * BigDecimal(pi)).toString())
    Log("pi / pi : ", (BigDecimal(pi) / BigDecimal(pi)).toString())
}

Running in the debugging tool:

2023-06-08 14:52:53     Info    pi / pi : 1
2023-06-08 14:52:53     Info    2.0 * pi : 6.283
2023-06-08 14:52:53     Info    oneGwei - pi : 999999996.8585
2023-06-08 14:52:53     Info    oneGwei + pi : 1000000003.1415
2023-06-08 14:52:53     Info    BigFloat(pi) : 3.1415
2023-06-08 14:52:53     Info    BigInt(oneGwei) : 1e+9
2023-06-08 14:52:53     Info    oneGweiForHex : 1e+9
2023-06-08 14:52:53     Info    oneGwei : 1e+9
2023-06-08 14:52:53     Info    pi : 3.14

Unit conversions

The following two functions: toAmount(), toInnerAmount() we have used many times in previous courses, these two functions are mainly used for data precision conversion.

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

The toAmount() function converts (reduces) a variable s according to the precision parameter decimals. In web3 practical development, it is often necessary to deal with some chained hexadecimal data.
We have often encountered this in our previous courses, for example, the data field data in the Transfer(address,address,uint256) event of a smart contract:

{
    "data": "0x00000000000000000000000000000000000000000000000001c1a55000000000",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000006b75d8af000000e20b7a7ddf000ba900b4009a80", "0x000000000000000000000000bcb095c1f9c3dc02e834976706c87dee5d0f1fb6"],
    "transactionHash": "0x27f9bf5abe3148169b4b85a83e1de32bd50eb81ecc52e5af006157d93353e4c4",
    "transactionIndex": "0x0",
    "removed": false,
    "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
    "blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
    "blockNumber": "0x109b1cc",
    "logIndex": "0x0"
}

When processing data "data": "0x00000000000000000000000000000000000000000000000001c1a55000000000", we use the toAmount() function. This processing is designed to do a good job of converting data field data to readable values.

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function main() {
    var data = "0x00000000000000000000000000000000000000000000000001c1a55000000000"
    Log(toAmount(data, 18))  // Print out 0.12656402755905127
}

1 ETH token, as we know, is 1e18 wei, if we get a data 126564027559051260 in wei, how to convert it to ETH tokens?
Using the toAmount(, 18) function is a very simple conversion method. The toInnerAmount() function is the reverse operation of the toAmount() function (depending on the precision, zoom in), and it is easy to convert the data using these two functions.

It is important to note the integer value safety range in the JavaScript language, Number.MAX_SAFE_INTEGER, and the following example illustrates a hidden problem when converting data:

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
    var amount = 0.01
    var innerAmount = Number(toInnerAmount(amount, 18))       

    Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER)  // 9007199254740991
    Log("innerAmount:", innerAmount)                          // 10000000000000000

    Log("typeof(innerAmount):", typeof(innerAmount), ", innerAmount:", innerAmount)
    
    // Decimal value 10000000000000000 -> Hexadecimal value 0x2386f26fc10000
    Log("Convert", innerAmount, "to hexadecimal:", innerAmount.toString(16))
    Log("Convert", BigInt(10000000000000000).toString(10), "to hexadecimal:", BigInt(10000000000000000).toString(16))
    
    Log("0x" + BigInt(10000000000000000).toString(16), "Convert to decimal:", toAmount("0x" + BigInt(10000000000000000).toString(16), 0))
}

It is possible to run in the debugging tool:

2023-06-15 16:21:40     Info    Convert 0x2386f26fc10000 to decimal: 10000000000000000
2023-06-15 16:21:40     Info    Convert 10000000000000000 to hexadecimal: 2386f26fc10000
2023-06-15 16:21:40     Info    Convert 10000000000000000 to hexadecimal: 10000000000000000
2023-06-15 16:21:40     Info    typeof(innerAmount): number , innerAmount: 10000000000000000
2023-06-15 16:21:40     Info    innerAmount: 10000000000000000
2023-06-15 16:21:40     Info    Number.MAX_SAFE_INTEGER: 9007199254740991

Through observation we found that:

Log("Convert", innerAmount, "to hexadecimal:", innerAmount.toString(16))

This line of code corresponds to the log output: Converting 10000000000000000 to hex: 10000000000000000, which is not converted correctly. The reason is naturally that 10000000000000000 is beyond Number.MAX_SAFE_INTEGER.

But when the decimal value is within the safe range, i.e., less than Number.MAX_SAFE_INTEGER, the toString(16) function converts it properly again, for example:

function main() {
    var value = 1000
    Log("Convert value to hexadecimal:", "0x" + value.toString(16))   // 0x3e8
    Log("Convert 0x3e8 to decimal:", Number("0x3e8"))               // 1000
}

In blockchain, even 0.01 ETH converted to a value of 10000000000000000 in wei will exceed Number.MAX_SAFE_INTEGER``, so a safer conversion for such cases is:BigInt(10000000000000000).toString(16)```.

Simulation Calls

Executing transactions and calling the Write method of smart contracts on Ethereum costs a certain amount of gas and sometimes it fails. It is important to know which transactions are likely to fail before sending them and calling them. There are simulated calls on Ethereum for testing.

eth_call

Ethereum's RPC method eth_call: it can simulate a transaction and return the result of a possible transaction, but it does not actually execute the transaction on the blockchain.

The eth_call method has 2 parameters, the first one is a dictionary structure, transactionObject:

// transactionObject
{
    "from" : ...,     // The address from which the transaction is sent
    "to" : ...,       // The address to which the transaction is addressed
    "gas" : ...,      // The integer of gas provided for the transaction execution
    "gasPrice" : ..., // The integer of gasPrice used for each paid gas encoded as hexadecimal
    "value" : ...,    // The integer of value sent with this transaction encoded as hexadecimal
    "data" : ...,     // The hash of the method signature and encoded parameters. For more information, see the Contract ABI description in the Solidity documentation
}

The second parameter is blockNumber: you can pass the label latest/pending/earliest, etc:

/* blockNumber
The block number in hexadecimal format or the string latest, earliest, pending, safe or 
finalized (safe and finalized tags are only supported on Ethereum, Gnosis, Arbitrum, 
Arbitrum Nova and Avalanche C-chain), see the default block parameter description in 
the official Ethereum documentation
*/

Next, we take the smart contract method approve and transfer calls of the token DAI as an example for simulation calls, and the following test environment is the main Ethereum network.

To be continued...

Coin Marketplace

STEEM 0.16
TRX 0.15
JST 0.029
BTC 56618.92
ETH 2337.15
USDT 1.00
SBD 2.40