以太坊的转账分主要分为为ETH转账、ERC20转账这2种,其转账流程,如图(1)所示:
ETH和ERC20这2种类型的代币,主要区别,发生在第3步"初始化原始的交易单rawTx{}"时,设置to字段和value字段有差别,如下表所示:
rawTx | ETH | ERC20 | 说明 |
---|---|---|---|
from | fromAddress | fromAddress | Token发送人 |
nonce | web3.utils.toHex(nonceNum) | web3.utils.toHex(nonceNum) | 交易的nonce值 |
gasLimit | web3.utils.toHex(8000000) | web3.utils.toHex(8000000) | 区块的gas个数上限 |
gasPrice | web3.utils.toHex(10e9) | web3.utils.toHex(10e9) | 单步计算所需的gas |
to | toAddress | contractAddress | 当币种为ETH时,填Token接收人;当币种为ERC20时,填合约地址 |
value | web3.utils.toHex(sendNum) | “0x0” | 当币种为ETH时,填ETH的个数; 当币种为ERC20时,置空 |
data | contractObj.methods.transfer(toAddress, sendNum).encodeABI() | contractObj.methods.transfer(toAddress, sendNum).encodeABI() | 发送Token,并编码 |
chainId | 0x04 | 0x04 | 网段为Rinkeby |
function getEthBalance(fromAddress, bookmark) {
web3.eth.getBalance(fromAddress).then(
function (wei) {
balance = web3.utils.fromWei(wei, 'ether')
console.log(bookmark + balance);
}
)
}
function getERC20Balance(fromAddress,contractObj,bookmark) {
contractObj.methods.balanceOf(fromAddress).call().then(
function (wei) {
balance = web3.utils.fromWei(wei, 'ether');
console.log(bookmark + balance);
});
}
对比如下:
3.1.1 创建一个文件夹名称为sendToken
mkdir sendToken
3.1.2 用npm和truffle初始化该该工程
cd sendToken
npm init -y
truffle init
3.1.3 修改sendToken\package.json文件,改成如下:
//package.json
{
"name": "sendToken",
"version": "1.0.0",
"description": "",
"main": "truffle-config.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@openzeppelin/contracts": "^2.5.0",
"@truffle/hdwallet-provider": "^1.1.1",
"bignumber": "^1.1.0",
"bignumber.js": "^8.1.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"eslint": "^5.15.0",
"ethereumjs-tx": "^1.3.7",
"request": "^2.88.2",
"web3": "^1.3.0"
},
"devDependencies": {
"@babel/core": "^7.12.3",
"@babel/preset-env": "^7.12.1"
}
}
3.1.4 安装依赖包
npm install
3.1.5 新建一个文件夹名称为myabi,然后在sendToken\myabi\目录下,新建一个ZTA_abi.json文件,该文件存放ZTA合约的abi
3.1.6 新建一个文件夹名称为projectConfig,然后在sendToken\projectConfig\目下,如下几个文件:
net.infuraKey 存放Infura节点的Key值
net.mnemonic 存放账户的助记词
net.prikey 存放账户的私钥
3.1.7 整个工程的目录结构如下:
将如下代码保存到sendToken\test\1.sendEth.js里,该代码是发送0.02ETH
//1.sendEth.js
var Tx = require('ethereumjs-tx');
var Web3 = require('web3')
var fs = require('fs');
const infuraKey = fs.readFileSync("./projectConfig/net.infuraKey").toString().trim();
var web3 = new Web3(new Web3.providers.HttpProvider(`https://rinkeby.infura.io/v3/` + infuraKey))
// web3 version
console.log(`web3 version: ${web3.version}`)
function getEthBalance(fromAddress, bookmark) {
web3.eth.getBalance(fromAddress).then(
function (wei) {
balance = web3.utils.fromWei(wei, 'ether')
console.log(bookmark + balance);
}
)
}
function getPriKey(prikeyPath) {
const privKeyFile = fs.readFileSync(prikeyPath).toString().trim();
const privKey = new Buffer.from(privKeyFile, 'hex');
return privKey
}
function getEthRawTx(fromAddress,toAddress,contractObj,sendNum,nonceNum, privKey) {
//raw Tx
var rawTransaction = {
"from": fromAddress,
"nonce": web3.utils.toHex(nonceNum),
"gasLimit": web3.utils.toHex(8000000),
"gasPrice": web3.utils.toHex(10e9),
"to": toAddress,
"value": web3.utils.toHex(sendNum),
"data": contractObj.methods.transfer(toAddress, sendNum).encodeABI(),
"chainId": 0x04 //4:Rinkeby, 3:Ropsten, 1:mainnet
};
var tx = new Tx(rawTransaction);
tx.sign(privKey);
var serializedTx = tx.serialize();
return serializedTx;
}
async function signTransaction(fromAddress,toAddress,contractObj,sendNum,nonceNum, privKey) {
var serializedTx = getEthRawTx(fromAddress,toAddress,contractObj,sendNum,nonceNum, privKey)
// Comment out these three lines if you don't really want to send the TX right now
console.log(`Attempting to send signed tx: ${serializedTx.toString('hex')}`);
var receipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'));
console.log(`Receipt info: ${JSON.stringify(receipt, null, '\t')}`);
}
function sleep(delay) {
return new Promise(reslove => {
setTimeout(reslove, delay)
})
}
//an async function
const sendEth = async () => {
//the number of sended token
//20 milli = 0.02 ether
var transferAmount = web3.utils.toWei('20', 'milli');
var myAddress = "0x1d75...a5f";
var destAddress = "0x8a40....23fb";
// get the nonce
var nonceCnt = await web3.eth.getTransactionCount(myAddress);
console.log(`num transactions so far: ${nonceCnt}`);
var abiArray = JSON.parse(fs.readFileSync("./myabi/ZTA_abi.json"));
var contractAddress = "0xAc194f047E43Ee0Ee10026C0B7AAA66489a0Ec45";
var contract = new web3.eth.Contract(abiArray, contractAddress, { from: myAddress });
// begin eth numbers of myAddress
await getEthBalance(myAddress, "Balance before send: ");
const privkey = getPriKey("./projectConfig/net.prikey")
await signTransaction(myAddress,destAddress,contract,transferAmount,nonceCnt, privkey)
sleep(100) //100ms
// end eth numbers of myAddress
await getEthBalance(myAddress, "Balance After send: ");
}
sendEth();
执行该nodejs脚本
node test\1.sendETH.js
效果如下:
也可以直接调用web3来发送ETH,即不调用合约来发送ETH,代码如下:
//3.sendETHv2.js
var Tx = require('ethereumjs-tx');
var Web3 = require('web3')
var fs = require('fs');
const infuraKey = fs.readFileSync("./projectConfig/net.infuraKey").toString().trim();
var web3 = new Web3(new Web3.providers.HttpProvider(`https://rinkeby.infura.io/v3/` + infuraKey))
// web3 version
console.log(`web3 version: ${web3.version}`)
function getEthBalance(fromAddress, bookmark) {
web3.eth.getBalance(fromAddress).then(
function (wei) {
balance = web3.utils.fromWei(wei, 'ether')
console.log(bookmark + balance);
});
}
function getPriKey(prikeyPath) {
const privKeyFile = fs.readFileSync(prikeyPath).toString().trim();
const privKey = new Buffer.from(privKeyFile, 'hex');
return privKey
}
function getEthRawTx(fromAddress,toAddress,sendNum,nonceNum, privKey) {
//raw Tx
var rawTransaction = {
"from": fromAddress,
"nonce": web3.utils.toHex(nonceNum),
"gasLimit": web3.utils.toHex(8000000),
"gasPrice": web3.utils.toHex(10e9),
"to": toAddress,
"value": web3.utils.toHex(sendNum),
"data": '',
"chainId": 0x04 //4:Rinkeby, 3:Ropsten, 1:mainnet
};
var tx = new Tx(rawTransaction);
tx.sign(privKey);
var serializedTx = tx.serialize();
return serializedTx;
}
async function signTransaction(fromAddress,toAddress,sendNum,nonceNum, privKey) {
var serializedTx = getEthRawTx(fromAddress,toAddress,sendNum,nonceNum, privKey)
// Comment out these three lines if you don't really want to send the TX right now
console.log(`Attempting to send signed tx: ${serializedTx.toString('hex')}`);
var receipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'));
console.log(`Receipt info: ${JSON.stringify(receipt, null, '\t')}`);
}
function sleep(delay) {
return new Promise(reslove => {
setTimeout(reslove, delay)
})
}
//an async function
const sendEth = async () => {
//the number of sended token
//20 milli = 0.02 ether
var transferAmount = web3.utils.toWei('20', 'milli');
var myAddress = "0x1d75...a5f";
var destAddress = "0x8a40....23fb";
// get the nonce
var nonceCnt = await web3.eth.getTransactionCount(myAddress);
console.log(`num transactions so far: ${nonceCnt}`);
// begin eth numbers of myAddress
await getEthBalance(myAddress, "Balance before send: ");
const privkey = getPriKey("./projectConfig/net.prikey")
await signTransaction(myAddress,destAddress,transferAmount,nonceCnt, privkey)
sleep(100) //100ms
// end eth numbers of myAddress
await getEthBalance(myAddress, "Balance After send: ");
}
sendEth();
将如下代码保存到sendToken\test\2.sendERC20.js里,该代码是发送0.02 ZTA
//2.sendERC20.js
var Tx = require('ethereumjs-tx');
var Web3 = require('web3')
var fs = require('fs');
const infuraKey = fs.readFileSync("./projectConfig/net.infuraKey").toString().trim();
var web3 = new Web3(new Web3.providers.HttpProvider(`https://rinkeby.infura.io/v3/` + infuraKey))
// web3 version
console.log(`web3 version: ${web3.version}`)
function getERC20Balance(fromAddress,contractObj,bookmark) {
contractObj.methods.balanceOf(fromAddress).call().then(
function (wei) {
balance = web3.utils.fromWei(wei, 'ether');
console.log(bookmark + balance);
});
}
function getPriKey(prikeyPath) {
const privKeyFile = fs.readFileSync(prikeyPath).toString().trim();
const privKey = new Buffer.from(privKeyFile, 'hex');
return privKey
}
function getERC20RawTx(fromAddress,toAddress,contractAddress,contractObj,sendNum,nonceNum, privKey) {
//raw Tx
var rawTransaction = {
"from": fromAddress,
"nonce": web3.utils.toHex(nonceNum),
"gasLimit": web3.utils.toHex(8000000),
"gasPrice": web3.utils.toHex(10e9),
"to": contractAddress,
"value": "0x0",
"data": contractObj.methods.transfer(toAddress, sendNum).encodeABI(),
"chainId": 0x04 //4:Rinkeby, 3:Ropsten, 1:mainnet
};
var tx = new Tx(rawTransaction);
tx.sign(privKey);
var serializedTx = tx.serialize();
return serializedTx;
}
async function signERC20Transaction(fromAddress,toAddress,contractAddress,contractObj,sendNum,nonceNum, privKey) {
var serializedTx = getERC20RawTx(fromAddress,toAddress,contractAddress,contractObj,sendNum,nonceNum, privKey)
// Comment out these three lines if you don't really want to send the TX right now
console.log(`Attempting to send signed tx: ${serializedTx.toString('hex')}`);
var receipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'));
console.log(`Receipt info: ${JSON.stringify(receipt, null, '\t')}`);
}
function sleep(delay) {
return new Promise(reslove => {
setTimeout(reslove, delay)
})
}
//an async function
const sendERC20 = async () => {
//the number of sended token
//20 milli = 0.02 ether
var transferAmount = web3.utils.toWei('20', 'milli');
var myAddress = "0x1d75...a5f";
var destAddress = "0x8a40....23fb";
// get the nonce
var nonceCnt = await web3.eth.getTransactionCount(myAddress);
console.log(`num transactions so far: ${nonceCnt}`);
var abiArray = JSON.parse(fs.readFileSync("./myabi/ZTA_abi.json"));
var contractAddress = "0xAc194f047E43Ee0Ee10026C0B7AAA66489a0Ec45";
var contract = new web3.eth.Contract(abiArray, contractAddress, { from: myAddress });
// begin token numbers
await getERC20Balance(myAddress,contract,"Balance before send: ");
const privkey = getPriKey("./projectConfig/net.prikey")
await signERC20Transaction(myAddress,destAddress,contractAddress,contract,transferAmount,nonceCnt, privkey)
sleep(100) //100ms
// end token numbers
await getERC20Balance(myAddress,contract,"Balance before send: ");
}
sendERC20();
执行该nodejs脚本
node test\2.sendERC20.js
效果如下: