By 古千峰
很多DAPP应用,会要求在电脑浏览器中安装以太坊钱包metamask
,当应用中需要发送ETH
时,自动打开metamask
,发送交易。
但是在浏览器中如果没有metamask
钱包(安装metamask
的门槛并不低,需要学会科学上网),该怎么办呢?能不能像imtoken
那样简单的操作呢?
发送交易的本质是用发送方的私钥签名,所以除了metamask
外,还可以使用另外两种方法签名:
- 导入
keystore
文本,然后输入对应的密码。
这也是imtoken
使用的主要方式,用过keystore
的都知道,每次发送交易,都需要输入一个密码。注意,这个密码并不是存放在钱包服务商服务器上的,而是加密后存放在keystore
文本中。一旦遗失了这个keystore
文件,就等于丢失了这个文件对应账户中资产的控制权。 - 导入
私钥
。
这个方式比较简单,直接将私钥输入即可。但是风险比keystore
方式高。因为几乎没人能够记住私钥,绝大多数人会把私钥保存在本地某个文件中,这样就使得私钥被盗的风险增加。而使用keystore
方式时,只要解开keystore
文件的密码没有被泄漏,账户还是安全的。
本教程,就先来介绍使用私钥
方式来签名并发送交易,而不需要安装metamask
钱包,这类应用可广泛用于手机端Dapp。这类Dapp不需要与运营商的服务器进行交互,前端源码公开,对于运营商来说,比较容易自证清白。
使用以太坊发送交易分为两步:
- 把交易内容json进行签名
- 发送签名后的数据
一、准备工作
1- 在编程之前,需要导入两个库:
-
web3.js
,推荐使用0.20.x
版本 -
ethereumjs-tx.js
,用于发送交易,即Tx
1.1 如果你喜欢使用nodejs
,用以下命令安装:
npm install web3@0.20.3 --save
npm install ethereumjs-tx --save
在程序中,使用以下命令导入库:
var Web3 = require("web3");
var Tx = require("ethereumjs-tx");
1.2 如果你喜欢使用原生javascript
,则需要在html
中使用如下方式导入以上两个库:
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
<script src="https://cdn.rawgit.com/ethereumjs/browser-builds/2fb69a714afe092b06645286f14b94f41e5c062c/dist/ethereumjs-tx.js"></script>
2- 注册infura
,并获取API KEY
开发以太坊Dapp,一般需要在本地安装geth
,但这种方法需要花比较多的时间和空间来同步区块,利用infura
可以简单很多,infura
提供公开以太坊和测试节点,可以利用infura
提供的api
访问以太坊以及IPFS
。
官网网址:infura.io。只需要提供email注册得到链接即可,进到infura
的dashboard
中,新建一个DAPP,即可获得API KEY
,如下图。
![](https://img.haomeiwen.com/i13960264/3b69cd3a2df86dc7.png)
二、连接以太坊网络
一般使用以下统一代码:
function init() {
var network = "mainnet";//或者ropsten等测试网络名称
var infura_apikey = "infura的API_KEY";
if (typeof web3 !== 'undefined') {
//如果浏览器中安装有metamask,则执行这里
web3 = new Web3(web3.currentProvider);
} else {
/*
//开发环境,如果在本地有以太坊的testrpc或者Ganache测试环境,则使用以下方式链接
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
*/
//通过infura连接以太坊网络
web3 = new Web3(new Web3.providers.HttpProvider('https://' + network + '.infura.io/v3/' + infura_apikey));
}
}
三、配置发送交易的数据
function getTx() {
//发送1个eth
var wei = web3.toWei("1", "ether");
//获取当前Gas价格,Gas价格一直变动,发送交易前建议动态获取
var gasPrice = web3.eth.gasPrice.toNumber();
//获取当前发送账号的所有交易,包括pending的交易,用来确定nonce值
//web3.eth.getTransactionCount 有bug,有时候无法正确获取,最好在客户端设置nonce值
var nonce = web3.eth.getTransactionCount('发送者地址', 'pending');
//获取估计的Gas值
var estimategas = web3.eth.estimateGas({
to: '收款方地址,或者合约地址',
data: '发送数据'
});
//获取gas限额
var gasLimit = web3.eth.getBlock("latest").gasLimit;
//配置交易参数
var rawTransaction = {
nonce: web3.toHex(nonce),
gasPrice: web3.toHex(gasPrice * 2),
gasLimit: web3.toHex(gasLimit),
gas: web3.toHex(estimategas),
value: web3.toHex(wei),
chainId: 3, //ropsten测试网络用3,主网用1
to: '收款方地址',
data: '发送数据'
}
return rawTransaction;
四、签名
如果是用nodejs
,使用如下代码:
var privateKey = new Buffer('发送账户的私钥', 'hex')
var tx = new Tx(rawTransaction);
tx.sign(privateKey);
var serializedTx = tx.serialize().toString('hex');//签名结果
如果用原生javascript
,使用如下代码:
var privateKey = new EthJS.Buffer.Buffer('发送账户的私钥', 'hex');
var tx = new EthJS.Tx(rawTransaction);
tx.sign(privateKey);
var serializedTx = tx.serialize().toString('hex');
五、发送交易
使用如下代码:
web3.eth.sendRawTransaction("0x" + serializedTx, function (err, hash) {
if (!err) console.log("Tx: " + hash); //交易发送成功后的TX值
else console.log(err.toString());
});
nodejs
版全部代码如下:
var Web3 = require('web3');
var Tx = require('ethereumjs-tx');
const network = "ropsten";//mainnet, ropsten
const chainId = 3;//1- mainnet, 3-ropsten
const sender = "0x615b80388e3d3cac6aa3a904803acfe7939"; //发送者公钥
const privateKey = new Buffer('19608a60b54943cd840c84b8e833ef84732a18eb562cdee09f8dba532ea', 'hex')
const toAddress = "0x3fe7995bf0a505a51196aa11218b181c849"; //接受者公钥
const ethAmount = "0";//发送的ETH
const hexData = "Hello World!";
const infura_apikey = "3446259cb0e74d68b614f9a103";
init();
sendTx();
function sendTx() {
var wei = web3.toWei(ethAmount, "ether");
var nonce = web3.eth.getTransactionCount(sender, 'pending');
var estimategas = web3.eth.estimateGas({
to: toAddress,
data: hexData
});
var gasPrice = web3.eth.gasPrice.toNumber();
var gasLimit = web3.eth.getBlock("latest").gasLimit;
var rawTransaction = {
nonce: web3.toHex(nonce),
gasPrice: web3.toHex(gasPrice * 2), //2倍Gas价格
gasLimit: web3.toHex(gasLimit),
gas: web3.toHex(estimategas),
value: web3.toHex(wei),
chainId: chainId,
to: toAddress,
data: hexData
}
var tx = new Tx(rawTransaction);
tx.sign(privateKey);
var serializedTx = tx.serialize().toString('hex');
console.log(serializedTx);
web3.eth.sendRawTransaction("0x" + serializedTx, function (err, hash) {
if (!err) console.log("Tx: " + hash);
else console.log(err.toString());
});
}
function init() {
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
//web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"));
web3 = new Web3(new Web3.providers.HttpProvider('https://' + network + '.infura.io/v3/' + infura_apikey));
}
}
如果使用原生javascript
,可以参考以下链接:
https://codepen.io/jackygu/pen/zJdVyj
网友评论