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

1. Last review and current plan

       In the last phase of development, we implemented the function of ERC20 token transfer. In this development, we implemented the function of signing transactions as planned. As I mentioned in the previous chapter, because our wallet is a web wallet, the way to implement signature transactions is a bit ugly. The user can only send the parameters of the transaction object through the query string and then analyze and sign the transaction. The essence of a signed transaction and an ETH transfer is actually the same. Both construct a transaction object first, and then use the private key to sign the transaction object. Regarding how to construct a transaction object, I have written a detailed explanation of how to construct an Ethereum transaction object in Js . Welcome to take a look at it when you have time.

       Suppose our wallet is running on localhost:3000, (that is, the development server). If we provide the following url:
http://localhost:3000/transfer?data=0x07391dd6000000000000000000000000000000000000000000000000000000000000000a&to=0x0cbde7fbf0f97726b804135fa638a86ceecae633&chainId=42

       The wallet will parse the query string and construct a transaction object, and then use the private key to sign and send the transaction. Of course, the user must log in first after the analysis is complete. The following is the signed transaction object page developed this time:
Insert picture description here
       Click to confirm, after the transaction is sent, you will enter the brief transaction information interface:
Insert picture description here
       here, click the back button to return to the main wallet interface.

2. Function realization

2.1 Implementation of UI

       The UI interface refers to the MetaMask interface, which is a bit cumbersome to put together. The main points are as follows:

       1. It is flexthe use of layout. flexThe most commonly used in layout is the alignment of elements, including the alignment on the major and minor axes. justifyContentAttribute for spindle alignment , generally set to space-betweenor center. The alignItemsattribute of the secondary axis is generally set to center. Such as the following code snippet:

const useStyles = makeStyles(theme => ({
    
    
    container: {
    
    
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
    },
    containerHeader: {
    
    
        display: 'flex',
        justifyContent: 'space-between'
    },
}));

       2. Another key point is the location attribute, which needs to be set flexibly according to the situation. Here is a brief introduction to the location attribute. In CSS, an element of the positionproperty has four static(默认值)values: fixed(固定值), , absolute(绝对的)and relative(相对的).

  • Static static means using normal flow layout. Static is not positional, so it will not be affected by top left bottom right and z-index.
  • The element with a fixed fixed position is the easiest to understand, it is always displayed and the position is calculated according to the window. Because our wallet only occupies a part of the size of the middle of the web, the entire wallet does not use fixed position elements.
  • The relative position is almost the same as the static position, and it also follows the normal layout flow. But it can use positional parameters such as top left bottom right.
  • The absolute position is similar to the fixed position, but it also determines the position based on the nearest non-static element.

       Among them, the relative position element can also provide a base point for the absolute position element, because the relative position is non-static. In order to adjust the height of the address Icon and the address in the wallet, we use the relative position. The code snippet is:

address:{
    
    
    position:'relative',
    marginLeft:theme.spacing(1),
    top:theme.spacing(0.5),
},

Another commonly used setting is padding and margin. As long as this is a front-end development, you have already mastered it.

2.2 Key points of code logic

       This time, the development is relatively simple. Except for the complicated UI stitching, the code logic is completed by making some modifications to the existing contents of the wallet. Mainly, two global variables are added to record the currently constructed transaction object and transaction response. Another point is to use the query string to obtain enough information to construct the transaction object.

2.2.1 Obtaining the value in the query string

There are many ways to get the query string in the url in React. This is how to get it in this wallet:
1. Use withRouterto package output components, mainly to provide locationattributes

export default withRouter(SignTransaction)

2. Use locationattributes in function components

function SignTransaction({
    
    history,location}) {
    
    
}

3, used URLSearchParamsto obtain the query string, it returns a query object, attribute values to obtain a specific getmethod

let search = location.search
let query = new URLSearchParams(search)
let transaction_info = convertQuery(query)
......
updateGlobal({
    
    
    transaction:transaction_info
})

2.2.2 Construction of trading objects

       This reader can first read the article I mentioned at the beginning of this article, of course, if the reader is already very skilled, you can skip that article.

const {
    
    isLogin,transaction,wallet} = useGlobal()
let trans = {
    
    
    ...transaction,
    gasPrice:utils.parseUnits("" + price,'gwei'),
    value:utils.parseEther("" + (transaction.value || 0)),
}

       Because we can allow users to reset the gasPrice, the gasPrice is replaced here. In addition, in order to simplify, we need to make an agreement. The values ​​sent from the url query string are all in decimal (not hexadecimal), and they are all common units. So value=0.01instead of in the query string value=10000000000000000. Similarly, gasPricethe value is also based on Gweithe unit number. chainIdThe value of is a decimal number, not a network name, because it only has 1,3,4,42, which is easy to remember. Note: dataThe value must be a hexadecimal string and start with '0x'.

2.2.3 Sending of transactions

       The signature of the transaction is the same as the ETH transfer, but the transaction object has a few more attribute values. After the transaction is submitted, some routing and navigation processing is required.

setCircleOpen(true)
//签名并发送交易
let provider = ethers.getDefaultProvider(net)
let tx_wallet = wallet.connect(provider)
tx_wallet.sendTransaction(trans).then(tx => {
    
    
    setCircleOpen(false)
    //todo 跳到交易信息界面
    updateGlobal({
    
    
        txGlobal:{
    
    
            tx,
            symbol:(!trans.data || trans.data==='0x') ? "ETH" : '',
            status:'pending',
        },
        transaction:null,
    })
    history.push('/send')
}).catch(err =>{
    
    
    setCircleOpen(false)
    return showSnackbar("交易发送失败",'error')
})

       In the code snippet, first open a progress bar animation to represent the transaction is being sent. When sendTransactionreturning, close the progress bar. Then set the global variables txGlobaland reset the trading object to nullprevent re-entering the trading interface (it is no longer needed at this time transaction, and the transaction has been sent). Finally navigate to SendEther.jsx. Let's look SendEther.jsxat the code snippet:

const {
    
    txGlobal} = useGlobal()
const [values,setValues] = useState({
    
    
    ...values_init,
    ...txGlobal
})
const {
    
    status,tx,amount,symbol} = values
if(status === BEGIN){
    
    
    return (
        <SendEtherForm cancelCallback={
    
    resverseBack} sendCallback={
    
    sendOver} />
    )
}else if(status === PENDING) {
    
    
    return (
        <TransactionInfo tx={
    
    tx} amount={
    
    amount} symbol={
    
    symbol} reverseCallback={
    
    resverseBack} />
    )
}else {
    
    
    return null
}

       Because we have set the status to'pending' before navigating, it will no longer display the sending interface, but the transaction information interface.

2.2.4 transferRouting processing

       Because our signature transaction interface is different from other interfaces and does not have the header of the wallet, we need to change the location of the route comparison to compare it at an earlier location transferinstead of comparing it in the wallet body. This is src\views\Main.jsxthe code snippet in:

import SignTransaction from './SignTransaction'
....
<Router >
    <Switch>
        <Route path='/transfer' component={
    
    SignTransaction} />
        <Route path='/'>
             <WalletBar />
             <Routes />
        </Route>
    </Switch>
</Router>

       From here, we can see that if it matches transfer, jump directly to the corresponding page; if not, first display the wallet title bar (that is, the logo and network switch button), and then the main content is matched again according to the route.

Three, a little experience

       There is not much content in this development, mainly UI stitching. Here is a little personal experience: if you feel that you divhave no bottom on which or other elements, you can set its background color to a lighter color, so that you can easily see its size, shape, range, etc. Or you can open the Chrome Developer Tools and you can see it, as shown below:
Insert picture description here
       Click in the Developer Tools Elements, you can see the entire HTML node tree, expand the nodes and click one of them div, you will see one on the left blue background marked block, it represents this div, and display it on the right margin, border, paddingand so on.

Four, summary

       This development mainly completes the realization of wallet signature transactions. The transaction is performed by passing the attribute value of the transaction object to the web page in the form of a query string, and the web page parses it. Due to the limitation of the web page version, a new page must be opened every time you sign, which is its shortcoming. Because the main purpose of our development of this wallet is to learn what it can do, not how well it can do, to learn.

       Although there is a transaction record in the main interface of the wallet, it is not currently planned for development. Interested readers can try it by themselves. The main purpose is to save the status of each transaction submission (including whether it is completed, transaction hash, etc.), save it under the network corresponding to the corresponding account, and also use local storage. After logging in to the wallet, go to get the status of these transactions and update them again.

       The functions in this wallet project have all been developed. Since we are developing one function one function at a time, we lack the overall design and coordination between each other, so we plan to spend some more time on it codereview. Mainly to optimize or simplify the code, and at the same time package it and publish it on the website.

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/104187318