005 以太坊钱包开发-账户转账

账户转账主要分为两部分:

  • 根据 privateKeykeystore 获取账户私钥及地址
  • 通过私钥签名交易实现转账

通过 privateKey 获取账户私钥及地址

通过调用 web3.eth.accounts.privateKeyToAccount(privateKey) 就可以通过私钥获取相应的用户信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async getAccountByPrivatekey(ctx) {

let returnResult = {
code: 0,
msg: '成功!',
data: {}
}

const data = ctx.request.body
const privateKey = data.privateKey

// 根据私钥获取用户地址
const account = web3.eth.accounts.privateKeyToAccount(privateKey)

// 查询账户的余额
const balance = await web3.eth.getBalance(account.address)
const ethNum = web3.utils.fromWei(balance, 'ether')

returnResult.data.address = account.address
returnResult.data.privateKey = account.privateKey
returnResult.data.balance = ethNum
ctx.body = returnResult
},

通过 keystore 获取账户私钥及地址

通过读取 keystore 里面存储的JSON数据及密码,通过调用 web3.eth.accounts.decrypt(keystoreStr,password) 可以获取用户的私钥及地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
async getAccountByKeystore(ctx) {

let returnResult = {
code: 0,
msg: '成功!',
data: {}
}

const data = ctx.request.body; // 获取上传文件

const keystoreFile = ctx.request.files.file
const filePath = keystoreFile.path

// 获取 keystore 里面的json字符串
const keystoreStr = await fileUtil.readFile(filePath)

// 获取账户的密码
const password = data.password

// 获取账户的信息地址及私钥
const account = web3.eth.accounts.decrypt(keystoreStr,password)

const balance = await web3.eth.getBalance(account.address)

const ethNum = web3.utils.fromWei(balance, 'ether')

returnResult.data.address = account.address
returnResult.data.privateKey = account.privateKey
returnResult.data.balance = ethNum

ctx.body = returnResult
}

获取 keystore 里面的json字符串,代码实现:

1
2
3
4
5
6
7
8
readFile(fPath) {
return new Promise(function (resolve, reject) {
fs.readFile(fPath, 'utf-8', function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}

上传keystore文件

页面通过 ajax 上传文件,需要构建 FormData,页面上传文件的js 代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function unlockByKeystore() {
var formData = new FormData();
formData.append('file', $('#file')[0].files[0]);
formData.append('password', $('#pwd').val());

$.ajax({
url: '/account/keystore',
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false
}).done(function(res) {
console.log(res)
if (res.code == 0) {
const result = res.data
const address = result.address
const balance = result.balance
const privateKey = result.privateKey

$("#balanceDiv").html(balance+' ETH')
$("#addressDiv").html(address)
$("#currAccount").val(address)
$("#currAccountKey").val(privateKey)
}
}).fail(function(res) {});
}

发送交易

发送交易主要分为以下几点:

  • 构建账单数据
  • 用私钥对帐单数据进行签名
  • 通过web3.eth.sendSignedTransaction 发送签名的交易。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
async sendTransaction (ctx) {
let returnResult = {
code: 0,
msg: '成功!',
data: {}
}

const data = ctx.request.body

const currentAccount = data.currAccount
const privateKey = data.privateKey
const reciptAccount = data.reciptAccount
const txValue = data.txValue
// 获取指定账户地址的交易数
let nonce = await web3.eth.getTransactionCount(currentAccount);

// 将 ether 转为 wei
let value = web3.utils.toWei(txValue,'ether');

// 获取当前gasprice
let gasPrice = await web3.eth.getGasPrice();

// 以太币转账参数
let txParms = {
from: currentAccount,
to: reciptAccount,
nonce: nonce,
gasPrice: gasPrice,
data: '0x00', // 当使用代币转账或者合约调用时
value: value // value 是转账金额
}
// 获取一下预估gas
let gas = await web3.eth.estimateGas(txParms);
txParms.gas = gas;
// 用密钥对账单进行签名
let signTx = await web3.eth.accounts.signTransaction(txParms,privateKey)

// 将签过名的账单进行发送
try {
await web3.eth.sendSignedTransaction(signTx.rawTransaction, function(error, hash){
if (!error) {
returnResult.data.hash = hash
} else {
returnResult.code = "101"
returnResult.msg = "失败!"
returnResult.data.error = error.message

}
})
} catch (error) {
console.log(error)
}

ctx.body = returnResult
}

修改路由文件

修改 routers/index.js 添加如下内容:

1
2
3
4
5
6
7
8
9
const transactionController = require("../controllers/transaction")

...

router.post('/account/privatekey',accountController.getAccountByPrivatekey)
router.post('/account/keystore',accountController.getAccountByKeystore)

router.get('/transaction', transactionController.transaction)
router.post('/transaction/send', transactionController.sendTransaction)

运行项目

启动私链网络

使用 geth 启动私有网络

1
2
3
4
$ geth --datadir ~/privatechain/data0 --networkid 110  --rpc console
```

开启本地挖矿

miner.start()

1
2

### 启动项目

$ cd myWallet
$ node index.js
`

访问 http://localhost:3000/transaction 查看项目:

源码下载

https://github.com/didianV5/web3EthWallet/tree/master/005_myWallet

谢谢你请我吃糖果