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:
Click to confirm, after the transaction is sent, you will enter the brief transaction information interface:
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 flex
the use of layout. flex
The most commonly used in layout is the alignment of elements, including the alignment on the major and minor axes. justifyContent
Attribute for spindle alignment , generally set to space-between
or center
. The alignItems
attribute 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 position
property 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 withRouter
to package output components, mainly to provide location
attributes
export default withRouter(SignTransaction)
2. Use location
attributes in function components
function SignTransaction({
history,location}) {
}
3, used URLSearchParams
to obtain the query string, it returns a query object, attribute values to obtain a specific get
method
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.01
instead of in the query string value=10000000000000000
. Similarly, gasPrice
the value is also based on Gwei
the unit number. chainId
The value of is a decimal number, not a network name, because it only has 1,3,4,42, which is easy to remember. Note: data
The 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 sendTransaction
returning, close the progress bar. Then set the global variables txGlobal
and reset the trading object to null
prevent 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.jsx
at 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 transfer
Routing 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 transfer
instead of comparing it in the wallet body. This is src\views\Main.jsx
the 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 div
have 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:
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
, padding
and 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.