前言
最近在写一个自动转账的脚本,经过了一番搜寻查阅及实战编写,总结了一些心得和见解,写出来希望能帮助到更多有需要的人。
以太币及合约币转账概念
一个以太坊账号包含三个部分,助记词(Mnemonic Phrase)
、私钥(Private Key)
以及 地址
,其中助记词与私钥是可以相互转换的。
由于私钥64位,长得太难看,没有可读性,而私钥的备份在电脑上复制起来容易,手抄下来就比较麻烦,但私钥保存在联网的电脑上毕竟不安全,有被其他人看到的风险,于是有了助记词工具,利用某种算法可以将64位私钥转换成十多个常见的英文单词,这些单词都来源于一个固定词库,根据一定算法得来。私钥与助记词之间的转换是互通的,助记词只是你的私钥的另一种外貌体现。
以太坊是一个分布式的智能合约平台,有一套 ERC-20 标准,通过此标准可以发行自己的合约币(Token),最早叫代币,后来统一翻译为通证。以太币是以太坊发行的币,主要是用来结算合约币(Token)交易费用。
以太坊有一套核心 EVM(以太坊虚拟机)
运行在分布式的智能合约平台,这套核心在区块链上每个参与的节点上运行,开发者可以在其上开发各种应用。与比特币的脚本引擎不同,以太坊的 EVM
功能非常强大,号称“图灵完备”。运行在 EVM
上的脚本称作 DApp(Decentralized Application)
,当我们发送一条交易时,这条交易会广播一条消息,收到这条消息的账户会运行消息相应的一系列指令,而运行指令的过程会消耗 gas
,当整个交易完成后会根据总共运行的指令量计算出 gasUsed
,gas
的单位为 Gwei
,运行不同的指令会干不同的或活,干不同的活所消耗的资源也不尽相同,这个表格 列举了以太坊的指令所对应消耗的 gas
量。
gas
这个名字起的非常贴切,翻译过来就是 汽油
的意思。如果把以太坊比做一台汽车,运行需要汽油驱动。汽油的价格称作 gasPrice
,车跑的过程所消耗的油量称作 gasUsed
,可以通过 gasLimit
来限制 gasUsed
的最大值,当超过这个值时就会终止,在终止之前所消耗的 gasUsed
依然会被扣除。如果直到交易完成也没触发或者恰好等于 gasLimit
,那么这个交易就会成功,交易完成后只扣除 gasUsed
。这里的 gasPrice
不像现实的汽油一样可以由自己控制,这是因为当你设置 gasPrice
油价越高时,你的交易就会被提前处理,举个不恰当的例子:相当于出高价可以买 98 的油,出低价只能买 92 的油一样。
gas
常用单位为 Gwei
,还有比它小的 Wei
,gas
在交易完成后会转换为 Ether
进行结算,具体转换如下:
1 | 1 Gwei = 1,000,000,000 wei |
每笔交易所消耗的
gas
不能提前计算获得,只能交易完成后才能确定。虽然不能提前知道,但是可以根据最近转账所使用的gasUsed
大概预估出来。
通过以上的学习我们可以做个小练习,在以太坊浏览器 https://etherscan.io 随便找个交易然后计算它的 gas
消耗,比如拿这个交易进行测试 0x20b727…866df6,只需要关注以下四个字段中的三个就能套用以上教程进行推导计算
key | value |
---|---|
Transaction Fee | 0.000393081 Ether ($0.06) |
Gas Limit | 30,237 |
Gas Used by Transaction | 30,237 (100%) |
Gas Price | 0.000000013 Ether (13 Gwei) |
1 | const gasPrice = 13; // Gwei |
最终结果:0.000393081 Ether,与 Transaction Fee 字段结果一样,说明我们的算法是没毛病的。
第三方接口及主要依赖库
转账业务逻辑编写
安装依赖包
1 | $ npm install bip39 eth-json-rpc-infura ethereumjs-wallet web3 |
创建 ./provider.js
,让 web3 支持 infura
,支持后可以链接到同步以太坊 mainnet
主网络
1 | const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider'); |
创建 ./client.js
,初始化 web3 对象
1 | const Web3 = require('web3'); |
转账需要密钥,如果你使用的是助记词(Mnemonic Phrase),需要先转换成密钥:
1 | const bip39 = require('bip39'); |
以太币转账逻辑
1 | const privateKey = Buffer.from('YOUR_PRIVATE_KEY', 'hex'); // 私钥 |
使用合约币(Token)转账,注意这里的 amount
合约币的转账数量需要乘上合约币发行时设置的精度(decimals)
1 | // 合约币地址 |
txParams 参数说明:
key | description |
---|---|
nonce | 发送者发送交易数的计数,按当前账号下的交易进行递增(从 0 开始),使用相同的 nonce 则可覆盖 pending 状态的交易,为了保险起见调用 web3.eth.getTransactionCount 方法获取当前账号交易计数 |
gasPrice | 发送者愿意支付执行交易所需的每个gas的Wei数量 |
gasLimit | 发送者愿意为执行交易支付gas数量的最大值。这个数量被设置之后在任何计算完成之前就会被提前扣掉 |
to | 接收者的地址。在合约创建交易中,合约账户的地址还没有存在,所以值先空着 |
value | 从发送者转移到接收者的Wei数量。在合约创建交易中,value作为新建合约账户的开始余额 |
init | 用来初始化新合约账户的EVM代码片段(只有在合约创建交易中存在)。init值会执行一次,然后就会被丢弃。当init第一次执行的时候,它返回一个账户代码体,也就是永久与合约账户关联的一段代码。 |
data | 消息通话中的输入数据,也就是参数(可选域,只有在消息通信中存在)例如,如果智能合约就是一个域名注册服务,那么调用合约可能就会期待输入域例如域名和IP地址 |
v,r,s | 用于产生标识交易发生着的签名 |
txParams.nonce
说明:
- 当nonce太小,交易会被直接拒绝。
- 当nonce太大,交易会一直处于队列之中,这也就是导致我们上面描述的问题的原因。
- 当发送一个比较大的nonce值,然后补齐开始nonce到那个值之间的nonce,那么交易依旧可以被执行。
- 当交易处于queue中时停止geth客户端,那么交易queue中的交易会被清除掉。
使用合约币转账注意事项:
txParams.to
地址要写合约币地址txParams.data
真正接收者的地址转换后写到这里
预估当前的转账价格
通过以下三种方式获取预估 gasPrice
:
- 使用第三方 API:https://ethgasstation.info/json/ethgasAPI.json
- 使用官方的 API,单位为 Gwei:https://www.etherchain.org/api/gasPriceOracle
- 调用的
web3.eth.getGasPrice()
方法获取,单位为 Wei,除于1e9
可以转换为 Gwei。这种方法只能获取的相当于以上第一种的average
字段与第二种的standard
字段,转账快,费用高。
这里讲下第一种方式,因为这种获取到的结果比较丰富一些,方便应对五花八门的需求,学会了这种另外两种自然也就会了,请求接口:https://ethgasstation.info/json/ethgasAPI.json,返回 JSON 中有以下字段:
字段 | 参考值 | 单位 | 说明 |
---|---|---|---|
fastest | 200 | 除 10 后为 Gwei | 转账速度很快,价格相对较贵 |
fast | 100 | 同上 | 转账速度一般,价格相对一般 |
safeLow | 30 | 同上 | 转账速度很慢,价格相对较低 |
fastestWait | 14.2 | 分钟 | 快速转账预计花费时间 |
fastWait | 2.2 | 同上 | 一般转账预计花费时间 |
safeLowWait | 2.2 | 同上 | 较慢转账预计花费时间 |
比如这里使用 safeLow
较慢转账的费用计算:
1 | const params = {from: myAddress, to: toAddress}; |
如果是合约币需换成以下 params
:
1 | const parmas = { |
或者直接使用上边初始化过的合约对象 contract
来获取会更简单一些:
1 | const estimategasUsed = contract.methods.transfer(toAddress, amount).estimateGas({from: myAddress}); |
合约币要注意的是
amount
不能大于账户实际的余额,要不然estimateGas
请求会得到一个 code 为-32000
的错误提示。
Ether(以太币)转法币的实时汇率可以通过这个接口获取:https://api.infura.io/v1/ticker/symbols,接口会返回一个所支持对法币的列表:
1 | { |
然后找个支持的 symbol 来获取具体的汇率,例如使用以太币对美元的 symbol 为 ethusd
,拼接后的 API 为:https://api.infura.io/v1/ticker/ethusd,返回示例:
1 | { |
因为数字货币的波动很大,这里有一个最新撮合的 bid
(买单价格)、 ask
(卖单价格),我们提取取 bid
作为汇率即可。
小结
- 如果 gasPrice 设置的小或者网络繁忙的话,一般超过 10 小时就会 drop 掉,如果需要保证转账成功率的话需要检测区块状态,
dropped
状态的要重新提交转账申请。 - 比特币为 10 分钟出一个块,一个块大概有 1M 左右;以太坊为 15 秒出一个块,块大小无限制。
至此结束,感谢阅读。