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

in #fmz6 months ago
  • gasPrice

    However, the gas fees on the Ethereum network always fluctuate according to market demand and the fees users are willing to pay, so writing a fixed gas fee in the code is sometimes not an ideal choice. We can use the eth_gasPrice method we learned before, which can obtain the average gas price.

  • gasLimit

    A standard Ether transfer has a gas limit of 21,000 units.

After understanding the concepts of nonce, gasPrice, and gasLimit, you can test the transfer. FMZ provides a very simple and easy-to-use transfer function.

exchange.IO("api", "eth", "send", toAddress, toAmount)

When it's used for transfers, the third parameter of exchange.IO is fixed as "send", the toAddress parameter is the address receiving ETH during the transfer, and toAmount is the amount of ETH transferred.

The parameters nonce, gasPrice, and gasLimit can all use system default values automatically obtained on FMZ. They can also be specified:

exchange.IO("api", "eth", "send", toAddress, toAmount, {gasPrice: 5000000000, gasLimit: 21000, nonce: 100})

Next, we will transfer a certain amount of ETH to a specific address on the goerli test network:

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

function main() {
    let walletAddress = exchange.IO("address")
    Log("Testnet goerli wallet address:", walletAddress)

    let ret = exchange.IO("api", "eth", "send", "0x4D75a08E870674E68cAE611f329A27f446A66813", toInnerAmount(0.01, 18))
    return ret // return Transaction Hash : 0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e
}

Because the unit of Ethereum transfer amount is wei, a custom function toInnerAmount needs to be used to process the value in wei units.

Query Transaction Hash: 0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e on https://etherscan.io/.

5.png

You can also write code to query transfer hash 0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e, using the eth_getTransactionReceipt method for queries.

function main() {
    let transHash = "0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e"
    let info = exchange.IO("api", "eth", "eth_getTransactionReceipt", transHash)
    return info
}

Query result:

{
    "cumulativeGasUsed": "0x200850",
    "effectiveGasPrice": "0x1748774421",
    "transactionHash": "0xa6f9f51b00d8ae850b0f204380b59da98f4bbce34b813577d3d948f61de4734e",
    "type": "0x0",
    "blockHash": "0x6bdde8b0f0453ecd24eecf7c634d65306f05511e0e8f09f9ed3f59eee2d06ac7",
    "contractAddress": null,
    "blockNumber": "0x868a50",
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "gasUsed": "0x5208",
    "to": "0x4d75a08e870674e68cae611f329a27f446a66813",
    "status": "0x1",
    "transactionIndex": "0x23",
    "from": "0x6b3f11d807809b0b1e5e3243df04a280d9f94bf4",
    "logs": []
}

Description corresponding to each field:

blockHash - The hash value of the block where the transaction is located.
blockNumber - The block number of the block where the transaction is located, encoded in hexadecimal.
contractAddress - If it's a contract creation, the address of the contract; otherwise null.
cumulativeGasUsed - The total gas used when executing this transaction in the block.
effectiveGasPrice - Total base fee plus tip per unit of gas.
from - Sender's address.
gasUsed - Gas used by this specific transaction.
logs - Array of log objects generated by this transaction.
  address - Address that generated this log.
  topics - Data array with 0 to 4 indexed log parameters, each with 32 bytes. In Solidity, first topic is event signature hash (e.g., Deposit(address,bytes32,uint256)), unless you declare an event using anonymous specifier.
  data - Non-indexed parameters for logs with length of 32 bytes.
  blockNumber - The block number of the block where this log is located.
  transactionHash - Transaction hash at time when log was created. Null if pending state.
  transactionIndex - Index position during creation. Null if pending state.
  blockHash - The hash value for containing block.
  logIndex - Hexadecimal-encoded integer index position within containing block. Null if pending state.
  removed - True if deleted due to chain reorganization; false for valid logs.
logsBloom - Bloom filter for retrieving related logs.
status - Hexadecimal-encoded value either being '1' (success) or '0' (failure).
to - Receiving party's address; null for contract creation transactions.
transactionHash - The hash value associated with given transaction.
transactionIndex - Hexadecimal-encoded index position within its respective containing-block.
type - Type value.

Call Ethereum Smart Contract

In the chapter on "Reading Contract Information", we used a complete example to call the method of ENS contract deployed on Ethereum to obtain Vitalik Buterin's wallet address. These methods belong to Read methods, and calling these methods does not require gas (remember what we talked about gas before?). In this chapter, we will call some Write methods of smart contracts on Ethereum and pay for gas. These operations will be verified by each node and miner in the entire network and change the blockchain state.

ERC20

For the ERC20 contract (ERC20 token contract), FMZ platform lists the ABI of the ERC20 contract ABI as a common ABI built directly into the system, eliminating the step of registering the ABI. We have also learned about ABI in previous tutorials when we registered ENS contract's ABI before calling ENS contract methods.

To better understand ABI, you can check it out before using it. Here is the ABI for ERC20 contracts:

[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","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":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]

This section uses the Goerli testnet environment.

balanceOf

Next, let's practice again how to call the Read method of a contract to read contract information, call the balanceOf method of ERC20 contract to query token balance, the balanceOf method has only one parameter, which is not named, but can be identified by its type as an address (i.e., the address of the token being queried). Since the returned data is not in units of one token, we also need the precision data of tokens for conversion. The precision of tokens can be obtained by using decimals method in ERC20 contracts. We will use Ethereum testnet goerli for testing. Please note that token contract addresses may vary on different chains.

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

function main() {
    let walletAddress = exchange.IO("address")
    
    // goerli WETH address 
    let wethAddress = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"
    // goerli LINK address 
    let linkAddress = "0x326C977E6efc84E512bB9C30f76E30c160eD06FB"

    // Since it is an ERC20 contract, FMZ has built-in ABI registration, so there is no need to register ERC20 ABI here.
    let wethDecimals = exchange.IO("api", wethAddress, "decimals")
    let linkDecimals = exchange.IO("api", linkAddress, "decimals")

    let wethBalance = exchange.IO("api", wethAddress, "balanceOf", walletAddress)
    let linkBalance = exchange.IO("api", linkAddress, "balanceOf", walletAddress)
    Log("WETH precision:", wethDecimals, "wethBalance:", toAmount(wethBalance, wethDecimals))
    Log("LINK precision:", linkDecimals, "linkBalance:", toAmount(linkBalance, linkDecimals))
}

By running the above code, you can query the current wallet's WETH and LINK token balance:

WETH precision: 18 wethBalance: 0
LINK precision: 18 linkBalance: 51.41374973681053

deposit

We can see that the balance of WETH tokens in the wallet is 0. Next, we will continue to interact with the ERC20 smart contract of WETH tokens, and this time we will call the Write type method: deposit.
The function of the deposit method is simply to exchange a certain amount of ETH for WETH. It should be noted that there are no parameters for the deposit method (which can be observed by checking ABI). The payable attribute of this method is true, so when calling it, you need to set the transferable ETH (payableAmount) quantity to specify the amount of ETH deposited.

Pay attention when calling smart contract methods:
If the called method has a payable attribute, you need to add a transfer ETH value (the fourth parameter of the exchange.IO function) after the method name, which can be a numeric type or a string form of numeric value.

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

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

function main() {
    let walletAddress = exchange.IO("address")
    
    // goerli WETH address 
    let wethAddress = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"

    // Since it is an ERC20 contract, FMZ has built-in ABI registration, so there is no need to register ERC20 ABI here.
    let wethDecimals = exchange.IO("api", wethAddress, "decimals")

    let wethBalance = exchange.IO("api", wethAddress, "balanceOf", walletAddress)
    Log("WETH precision:", wethDecimals, "wethBalance:", toAmount(wethBalance, wethDecimals))
    let ethBalance = exchange.IO("api", "eth", "eth_getBalance", walletAddress, "latest")
    Log("ETH precision:", 18, "ethBalance:", toAmount(ethBalance, 18))

    // Call the deposit method, since deposit is a method outside of the ERC20 standard, we need to register the ABI for this method here
    let abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","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":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]`
    exchange.IO("abi", wethAddress, abiWETH)

    let payableAmount = toInnerAmount(0.01, 18)
    let ret = exchange.IO("api", wethAddress, "deposit", payableAmount)
    Log("Transaction Hash:", ret)
}

WETH precision: 18 wethBalance: 0
ETH precision: 18 ethBalance: 0.14333094664072302
Transaction Hash: 0xaf15b0b0e25a81eda583295e82b249e2d02e4eafebecc906470ccc2c89e23563

6.png

Check the balance of WETH and ETH again:

WETH precision: 18 wethBalance: 0.01
ETH precision: 18 ethBalance: 0.1333309358060905

Before calling deposit, WETH is 0 and ETH is 0.14333094664072302. After calling deposit, WETH is 0.01 and ETH is 0.1333309358060905. It can be seen that it has exchanged 0.01ETH for WETH successfully.

transfer

ERC20 tokens can also be transferred, using the transfer method to transfer 0.01 WETH tokens to the address 0x4D75a08E870674E68cAE611f329A27f446A66813. The transfer method has two parameters, the first parameter dst is the wallet address of the transfer recipient, and the second parameter wad is the transfer amount.

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

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

function waitMined (tx) {
    for (var i = 0 ; i < 10 ; i++) {
        Sleep(5000)
        let info = exchange.IO("api", "eth", "eth_getTransactionReceipt", tx)
        if (info && info.gasUsed) {
            Log(info)
            return true
        }
        Log('Transaction not yet mined', tx)
    }
    return false 
}

function main() {
    let walletAddress = exchange.IO("address")
    
    // goerli WETH address 
    let wethAddress = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"

    // Since it is an ERC20 contract, FMZ has built-in ABI registration, there is no need to register ERC20 ABI here.
    let wethDecimals = exchange.IO("api", wethAddress, "decimals")
    let wethBalance = exchange.IO("api", wethAddress, "balanceOf", walletAddress)
    Log("WETH precision:", wethDecimals, "wethBalance:", toAmount(wethBalance, wethDecimals))

    let dst = "0x4D75a08E870674E68cAE611f329A27f446A66813"
    let wad = toInnerAmount(0.01, wethDecimals)
    let tx = exchange.IO("api", wethAddress, "transfer", dst, wad)   
    Log("Transaction Hash:", tx)
    waitMined(tx)
    
    wethBalance = exchange.IO("api", wethAddress, "balanceOf", walletAddress)
    Log("WETH precision:", wethDecimals, "wethBalance:", toAmount(wethBalance, wethDecimals))
}

In the above example, a custom function waitMined is used. In fact, we are not unfamiliar with the function of this function. Do you remember the eth_getTransactionReceipt method? The purpose of this waitMined custom function is to wait for the result of the passed Transaction Hash.

For this WETH transfer, interested readers can also query the Transaction Hash 0x2fafb62b548a1fffb0f3189429e3c5a4f57ddafb0acbc0678d8b3cf0a2f7c92c to view details (note that it is on testnet goerli).

withdraw

This time we will exchange WETH back to ETH, using the withdraw method, which has only one parameter wad, simply put, it is used to set how much ETH to exchange back.

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

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

function main() {
    let walletAddress = exchange.IO("address")
    
    // goerli WETH address 
    let wethAddress = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"

    // Since it is an ERC20 contract, FMZ has built-in ABI registration, there is no need to register ERC20 ABI here.
    let wethDecimals = exchange.IO("api", wethAddress, "decimals")

    let wethBalance = exchange.IO("api", wethAddress, "balanceOf", walletAddress)
    Log("WETH precision:", wethDecimals, "wethBalance:", toAmount(wethBalance, wethDecimals))
    let ethBalance = exchange.IO("api", "eth", "eth_getBalance", walletAddress, "latest")
    Log("ETH precision:", 18, "ethBalance:", toAmount(ethBalance, 18))

    let abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","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":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]`
    exchange.IO("abi", wethAddress, abiWETH)

    let wad = toInnerAmount(0.01, 18)
    let ret = exchange.IO("api", wethAddress, "withdraw", wad)
    Log("Transaction Hash:", ret)
}

Transaction Hash: 0x446423c841451a8d04428a075b556eb5564186b09926da915f5da1c9837d2af4

From the above code, it can be seen that it is basically the same as the previous example, except that the method called in the last step is changed to withdraw, before calling:

WETH precision: 18 wethBalance: 0.01
ETH precision: 18 ethBalance: 0.11322979983231546

Query again:

WETH precision: 18 wethBalance: 0
ETH precision: 18 ethBalance: 0.1231207156449464

We can see that 0.01 WETH has been exchanged back to ETH.

To be continued...

Sort:  

You've got a free upvote from witness fuli.
Peace & Love!

Coin Marketplace

STEEM 0.16
TRX 0.13
JST 0.027
BTC 60535.76
ETH 2598.15
USDT 1.00
SBD 2.54