Learn while using--use the Material UI framework under React to develop a simple MetaMask-like web version of the Ethereum wallet (8)

       In the last development, when we displayed the ERC20 token list of the user account, we missed a function, which is to update the user's token balance while displaying the list. In this development, we will add this function first, and then complete the transfer function of ERC20 tokens.

1. Update user ERC20 token balance

       In the last development, the user's ERC20 token balance was only obtained once when adding tokens. In the display list interface, only the user token change event is monitored, but if this event is not broadcast when the wallet is opened, it cannot be monitored, so the token balance of the user account will not be updated. And according to the normal logic, the latest token balance should be automatically displayed when the user token list is displayed, as shown below:
Insert picture description here

       The usual way to perform similar operations in the React function component is to use it after the component is loaded useEffectto obtain the user token balance, and then update the corresponding token information. Since obtaining the token balance requires other token information, such as token address, etc., this useEffectdependency for updating token information needs to use the token information itself. This will cause an infinite loop, the specific process is as follows:

       After the component is loaded, the token balance is displayed => get the latest token balance, update the token information => re-render the component => useEffectthe content of the re-execution caused by the update of the dependency token information => get the token balance again and update it => Render and reacquire again, endless loop.

       So we must use a state variable to control this repeated acquisition of user token balances. First, set this variable to false. If the balance is obtained, set the variable to true before updating the balance. After updating the balance and re-rendering, check whether the variable is false. If it is not, it means that it has been updated and skip updating again. At the same time, the user account or network changes should reset this variable to false, so that the latest balance of the tokens corresponding to other accounts or networks can be obtained. Let's take a look at the code snippet:

const [isRefresh,setIsRefresh] = useState(false)

//每次切换网络或者用户时,更新刷新状态
useEffect(()=> {
    
    
    if(wallet && network){
    
    
        setIsRefresh(false)
    }
},[wallet,network])

//每次打开界面时只更新一次代币余额
if(!isRefresh) {
    
    
    Promise.all(allPromise).then(results => {
    
    
        //更新所有代币余额
        for(let i=0;i<len;i++) {
    
    
            tokens[i].balance = results[i]
        }
        if(!stale) {
    
    
            setIsRefresh(true)
            updateAllTokens(wallet.address,network,tokens)
        }
    }).catch( () =>{
    
    })
}

       The first line of code here is to set the state variable used to mark whether the token balance has been obtained. The second code is to reset the status when the account or network changes, and the third code is to update all token balances if the status is false.

       The update code can also be placed in the background, that is, in src\contexts\StorageProvider.jsthe process, the update processing is similar to the user ETH balance update processing. However, the user token balance is only used when displaying the token list and sending tokens. In order to reduce the number of refreshes, it is placed on the corresponding interface for processing.

2. Brief description of this development

       This development is mainly to realize the function of user token transfer. The development this time is not difficult, mainly because some modifications have been made on the basis of the ETH transfer that has been implemented previously.

       Click on any token in the user's ERC20 representative to enter the main interface of the wallet and display the token:
Insert picture description here
       Our main interface in the picture above shows GEC tokens, and its balance will be automatically updated once it is displayed. At the same time, it also monitors transaction events to update in real time. The processing method is similar to the method of updating user token balance mentioned above.

       When we click the send button, an interface similar to sending ETH will appear:
Insert picture description here
       we fill in the recipient's address and the quantity to be sent, the quantity can also be a decimal. Then click the send button, a confirmation popup will appear, click OK, and a brief transaction information interface will appear after a short loading animation.

       Note: No matter whether sending ETH or tokens, there is a fee, and a small amount of ETH is consumed. If the sending fails, return to the main interface of the wallet to check if you have enough ETH.

       Note: You can switch networks in this interface. Since the tokens in one network do not exist in another network, and ETH does exist, the wallet will automatically become sending ETH after switching the network. Here, the user needs to be careful not to send wrong.
Insert picture description here
       Switch the network to the Kovan test network, and you can see that sending 100 GEC becomes 100 ETH, as shown above. Because my ETH quantity is not that many, the first one cannot be sent out, and the second wallet will prompt. But if the user sends 1 GEC, here it becomes 1 ETH, and there is still a good chance to send it out.

       Let's click Cancel to return to the main interface. Following the above operation, the network has become the Kovan test network at this time, and we have to test on this network, click the menu button in the upper left corner:
Insert picture description here
       let's click FIXED token, enter the main interface again to restart the sending process:
Insert picture description here
       click Send, a confirmation button will pop up, allowing users to check the last before sending to prevent sending errors.
Insert picture description here
       Click OK, then a loading animation will be displayed, and the token contract on Ethereum will be called for token transfer. After the transaction is submitted, you will enter the brief transaction information interface:
Insert picture description here
       Compared with the transaction information when sending ETH, we have added a transaction type information. At present, the positions of some texts on this interface are not very aligned, and need to be adjusted carefully.

       Wait patiently for the transaction to be completed, because the Kovan testnet has a fast block generation rate, so the transaction should be confirmed soon. The following is the completed interface. At this time, the transaction status has been updated and the pending animation has stopped.
Insert picture description here
       Click the back button to return to the main interface of the wallet, because our token balance is updated after entering the main interface, so you can also see the token balance update action on this interface, that is, 2090 becomes 1990 in the figure below.
Insert picture description here
       Well, the main functions of this development are introduced. For the complete code, you still go to the git repository. Here are just a few points.

Third, the introduction of code points

  1. We added a global variable to tokenSelectedIndexrepresent the currently selected token, such as ETH or GEC. The currency currently selected in the token list will have a background prompt.
const {
    
    network,wallet,ethPrice,tokenSelectedIndex} = useGlobal()

       At the end of the development function introduction, the main interface of our wallet displays FIXED tokens, so click the menu button in the upper left corner, and the FIXED tokens in the token list will be displayed as selected. If we do not select any tokens, ETH is selected by default, and if we hide the currently selected tokens, then ETH is also selected.
Insert picture description here
       Every time we switch networks, we are set to choose ETH, because the tokens of one network do not exist in another network.

const handleSelected = key => () => {
    
    
        if(selectedIndex === key) {
    
    
            return;
        }
        setSelectedIndex(key)
        setOpen(false);
        updateGlobal({
    
    
            tokenSelectedIndex:0,
            network:NET_WORKS[key]
        })
    };

       A closure is used here. Because there is no this in the function component, it cannot use this.handleSelected.bind(this,key)this method commonly used in class components to pass parameters while binding the function. The common way to bind click events in function components is to use closures to generate a new function for parameter passing. Another method is to set a specific clicks on an object valueproperty, when the click function takes a binding e.currentTarget.valuevalue. But this value is not always available. Sometimes it is more convenient to use closures. It is recommended that you use closures to bind click events when you need to pass parameters.

  1. Call the contract to send tokens, the code here briefly explains:
if(isToken) {
    
    
    //将钱包绑定合约直接调用方法进行
    let contract = getErc20Token(token.address,network,wallet)
    if(contract) {
    
    
        let amount = convertFixedToBigNumber(eth_amount,token.decimals)
        let args = [_address,amount]
        contract.transfer(...args).then(tx => {
    
    
            setCircleOpen(false)
            if(sendCallback){
    
    
                sendCallback(tx,eth_amount,SYMBOL)
            }
        }).catch(err =>{
    
    
            setCircleOpen(false)
            return showSnackbar(SYMBOL + "发送失败",'error')
        })
    }else{
    
    
        setCircleOpen(false)
        return showSnackbar("合约未初始化,请稍候",'info')
    }
}

       The third line of code is to obtain a contract object, which is bound to the corresponding wallet, that is, the bound contract can call vieweither the puremethod for reading contract data or the method for rewriting contract data. In fact, contract.transfer(...args)this function call can also have an additional optional parameter, which is options. It is similar to the transaction object created during direct ETH transfer mentioned in the previous article. For example, you can set the amount of ETH sent with the transaction, set the chainId, and so on.

Four, summary

       This development first made up an omission from the previous development, and then appropriately modified it based on the ETH transfer for ERC20 token transfer. The main exercise useEffectis to prevent unlimited updating of user token balance and how to call the contract to transfer tokens. There is another imperfection in this wallet that has not been modified, that is, the eth price is taken from etherscan. The etherscan API cannot be accessed without a ladder. It is planned to change it to another data source during the next development.

       Our wallet now has functions: account creation, login, import and export; add, display, hide and send ERC20 tokens, display and update the ERC20 token balance in real time; send ETH and update the ETH balance in real time; support the main network and The three major test networks can actually support the Goerli test network, but there is no need to make so many. Currently, it does not support multiple accounts and historical record storage.

       There is only one function of our wallet that has not been implemented from the development plan, and that is signature transactions. Because this is a simple web wallet, transaction data can only be sent via URL and then signed. The implementation method is a bit ugly. We plan to implement it in the next development.

       In addition, due to limited time, the application of the local localhost node is not planned for development. It is planned to cancel the localhost network in the next development, and then add it when you have time to develop it.

This wallet code cloud (gitee) warehouse address is: => https://gitee.com/TianCaoJiangLin/khwallet

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

Guess you like

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