如何使用 web3js 发送 ERC20 令牌
How to send an ERC20 token with web3js
我目前正在使用 0.2x.x 版本的 Web3 JavaScript API。我通过在 solidity 中创建智能合约(在 REMIX IDE 上)部署了我的自定义 ERC20 代币。
我安装了 MetaMask 并在 https://wallet.ethereum.org/ 上进行了测试,以将一些自定义 ERC 令牌发送到我的另一个帐户。它运作良好。
我想使用 Web3js 在我的 JavaScript 代码中添加 'send custom ERC20 Token' 函数。
下面是我的代码。
var http = require('http');
var Web3 = require('web3');
var Tx = require('ethereumjs-tx');
var abi = [{"...."}];
var data = '0x00..';
var contract_addr = '0x00..';
var owner = '0x00..';
var web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/"));
web3.eth.getBlock("latest", (error, result) => {
//console.log('error:', error);
//console.log('results', result);
});
var myContract = web3.eth.contract(abi);
var myContractInstance = myContract.at(address);
eb3.eth.getDefaultAccount = owner;
var defaultAccount = web3.eth.getDefaultAccount;
console.log('DefaultAccount => ', defaultAccount);
var total = myContractInstance.totalSupply();
var balance = myContractInstance.balanceOf(defaultAccount);
console.log('DefulatAccount total => ',total);
console.log('DefaultAccount balance => ',balance);
var to = '0x00..';
var isAddress = web3.isAddress(to);
console.log('isAddress(to) => ',isAddress);
console.log('balanceOf(to) => ',myContractInstance.balanceOf(to));
我正在研究 'Ropsten Testnet',我发送了一些 'ether' 用于使用 sendRawTransaction()
进行测试。但我想做的只是发送我的自定义 ERC20 令牌。不是以太。如何在此脚本中发送我自己的令牌?我不知道。我尝试像下面那样使用 sendTransaction()
。
var rawTx = {
from : defaultAccount,
nonce : nonceHex,
gasPrice : gasPriceHex,
gas : gasHex,
to : to,
value : 100,
data : data
};
web3.eth.sendTransaction(rawTx, function(err, transactionHash) {
if (!err)
console.log('transactionHash => ',transactionHash);
else
console.log(err);
});
我不断收到错误。
Error: Invalid JSON RPC response: ""
at Object.InvalidResponse (/Users/rachel/dev/test/node_modules/web3/lib/web3/errors.js:38:16)
at XMLHttpRequest.request.onreadystatechange (/Users/rachel/dev/test/node_modules/web3/lib/web3/httpprovider.js:119:24)
at XMLHttpRequestEventTarget.dispatchEvent (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:64:18)
at XMLHttpRequest._setReadyState (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:354:12)
at XMLHttpRequest._onHttpResponseEnd (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:509:12)
at IncomingMessage.<anonymous> (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:469:24)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1056:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
实在想不明白这有什么问题。请让我知道一些方法来处理这个问题。谢谢!
那么你可以尝试这样的事情-:
`myContractInstance.transfer(to,amount, {gas:estimateGas,
gasPrice:gasPrice,from:fromAddr, nonce: nonceis}, function(err,txhash)
{
console.log("error: "+err);
console.log("txhash: "+txhash);
}
`
这里'to'是你要发送代币的地址
'amount' 您要发送的代币数量
'estimateGas' 交易执行的 gas limit
'gasPrice' 每个 gas 单位的价格 wei
'fromAddr' 您要发送这些令牌的地址。由于交易将使用此地址签名,因此这应该是在 metamask 中选择的地址或传递给 infura
'nonceis' 交易的随机数
以EOS代币转账为例
下面的代码需要 web3
和 ethereumjs-tx
。如果您还没有安装它们,请使用 npm install web3 ethereumjs-tx
安装它们
var Tx = require('ethereumjs-tx');
var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545/'))
// set token source, destination and amount
var myAddress = "0xaa597b7e8aaffe9f2a187bedb472ef3455957560"
var toAddress = "0xa013927bffe9e879134061b9330a01884a65497c"
var amount = web3.utils.toHex(1e16)
// get transaction count, later will used as nonce
web3.eth.getTransactionCount(myAddress).then(function(v){console.log(v); count = v})
// set your private key here, we'll sign the transaction below
var privateKey = new Buffer('6d...', 'hex')
// Get abi array here https://etherscan.io/address/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0#code
var abiArray = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name_","type":"bytes32"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"symbol_","type":"bytes32"}],"payable":false,"type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]
// Here you may get the abicode from a string or a file, here is a string case
// var abiArray = JSON.parse('[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name_","type":"bytes32"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"symbol_","type":"bytes32"}],"payable":false,"type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]', 'utf-8')
var contractAddress = '0x86Fa049857E0209aa7D9e616F7eb3b3B78ECfdb0'
var contract = new web3.eth.Contract(abiArray, contractAddress, {from: myAddress})
var rawTransaction = {"from":myAddress, "gasPrice":web3.utils.toHex(2 * 1e9),"gasLimit":web3.utils.toHex(210000),"to":contractAddress,"value":"0x0","data":contract.methods.transfer(toAddress, amount).encodeABI(),"nonce":web3.utils.toHex(count)}
var transaction = new Tx(rawTransaction)
transaction.sign(privateKey)
web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'))
// check the balance
contract.methods.balanceOf(myAddress).call().then(function(balance){console.log(balance)})
您还可以使用 https://etherscan.io 查看交易。
交易发送后,您可以使用https://etherscan.io/address/0xaa597b7e8aaffe9f2a187bedb472ef3455957560#tokentxns进行查看,您会在Transactions
字段中看到如下待处理交易,但在[=15]中看不到=] 字段.
稍等1分钟,Transactions
字段和Token Transfers
字段都可以看到成功
错误:无效 JSON RPC 响应:“”表示 web3 无法连接到 rpc 节点。
尝试 运行 一个 ::
-测试网
geth --testnet --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpccorsdomain '*' --rpcapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --ws --wsaddr 127.0.0.1 --wsport 8546 --wsorigins '*' --wsapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --maxpeers 25 0 --gasprice 100
-主网
geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpccorsdomain '*' --rpcapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --ws --wsaddr 127.0.0.1 --wsport 8546 --wsorigins '*' --wsapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --maxpeers 25 0 --gasprice 100
现在在你的情况下(使用 infura testnet 节点),从 infura.io 获取一个 id。
应该是这样的::
https://ropsten.infura.io/id
您需要调用ERC20合约的transfer方法。
例如:
myContractInstance.methods.transfer(toAddress, amount);
并且在您的原始交易对象中:
- 您不需要金额
- 地址应该是合约地址(不是方法调用中的toAddress)
例如:
var rawTx = {
from : defaultAccount,
nonce : nonceHex,
gasPrice : gasPriceHex,
gas : gasHex,
to : <contractAddress>,
data : data
};
我们只需要 'ethers' 就可以做到。
在这种情况下,我将后端 api 与节点 js 一起使用。
const fs = require('fs');
const express = require('express');
const router = express.Router();
const { ethers, BigNumber } = require('ethers');
const ethUtil = require('ethereumjs-util');
const BridgeEth = require("../../client/src/components/contract/EthBridge.json");
const BridgeBsc = require("../../client/src/components/contract/BridgeBsc.json");
const addresses = require("../../client/src/components/contract/config.json");
const eth_bridge = addresses.eth_bridge;
const bsc_bridge = addresses.bsc_bridge;
const net_config = require("../../net.config.json");
const ethnet = net_config.eth_url;
const bscnet = net_config.bsc_url;
const eth_provider = new ethers.providers.JsonRpcProvider(ethnet);
const bsc_provider = new ethers.providers.JsonRpcProvider(bscnet);
const eth_contract = new ethers.Contract(eth_bridge, BridgeEth, eth_provider);
const bsc_contract = new ethers.Contract(bsc_bridge, BridgeBsc, bsc_provider);
var privateKey = fs.readFileSync('./secret', 'utf-8');
const adminaccount = {
publicKey: net_config.admin_public_key,
privateKey: privateKey
}
console.log(`Admin account is ${adminaccount.publicKey}`);
const adminEthWallet = new ethers.Wallet(adminaccount.privateKey, eth_provider);
const adminBscWallet = new ethers.Wallet(adminaccount.privateKey, bsc_provider);
const signedEthContract = eth_contract.connect(adminEthWallet);
const signedBscContract = bsc_contract.connect(adminBscWallet);
const sig = "0x6162636400000000000000000000000000000000000000000000000000000000";
router.post('/eth2bsc', async (req, res) => {
var account = req.body.account;
var amount = req.body.amount;
var signature = req.body.signature;
var msg = req.body.hash;
const isValidAddress = ethUtil.isValidAddress(account);
const isValidAmount = amount > 0;
const isValidSign = ethUtil.isHexPrefixed(String(signature));
const isValidHash = ethUtil.isHexPrefixed(String(msg));
if (!(isValidAddress && isValidAmount && isValidSign && isValidHash)) {
return res.status(500).json({ step: 1, message: `Security error! Server received invalid prams!${isValidAddress}, ${amount}, ${isValidSign}, ${isValidHash}` });
}
var msgBuffer = '';
var msgHash = '';
var signatureBuffer = '';
try {
msgBuffer = ethUtil.toBuffer(msg);
msgHash = ethUtil.hashPersonalMessage(msgBuffer);
signatureBuffer = ethUtil.toBuffer(signature);
} catch (e) {
return res.status(500).json({ step: 1, message: 'Security error! Request with invalid params!' })
}
const signatureParams = ethUtil.fromRpcSig(signatureBuffer);
const publicKey = ethUtil.ecrecover(msgHash, signatureParams.v, signatureParams.r, signatureParams.s);
const adddressBuffer = ethUtil.publicToAddress(publicKey);
const address = ethUtil.bufferToHex(adddressBuffer);
console.log(`recvered address is ${address}`);
const isMatched = account.toLowerCase() == address.toLowerCase();
if (!isMatched) {
return res.status(500).json({ step: 1, message: 'Security error! Transaction caller is not signer!' })
}
console.log(`I will burn BSCFloki from ${account}`)
try {
var tx = await signedEthContract.burn(account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig)
console.log(tx.hash);
console.log("First transaction successed(ETHFloki burned)");
} catch (e) {
console.log("Transaction to burn ETHFloki faild");
return res.status(500).json({ step: 1, message: 'Transaction Faild! \nCheck your account and token balance.' })
}
console.log(`I will mint BSCFloki to ${account}`)
try {
var tx1 = await signedBscContract.mint(account, account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig);
console.log(tx1.hash);
console.log("Second transaction successed(BSCFloki minted)");
} catch (e) {
console.log("Transanction to mint BSCFloki faild");
return res.status(500).json({ step: 2, message: 'Transaction Faild! \nCheck your account.' })
}
return res.send(`Success! \n ${amount} ETHFloki converted to ${amount} BSCFloki in your wallet.`);
});
router.post('/bsc2eth', async (req, res) => {
var account = req.body.account;
var amount = req.body.amount;
var signature = req.body.signature;
var msg = req.body.hash;
const isValidAddress = ethUtil.isValidAddress(account);
const isValidAmount = amount > 0;
const isValidSign = ethUtil.isHexPrefixed(String(signature));
const isValidHash = ethUtil.isHexPrefixed(String(msg));
if (!(isValidAddress && isValidAmount && isValidSign && isValidHash)) {
return res.status(500).json({ step: 1, message: 'Security error! Server received invalid prams!' });
}
var msgBuffer = '';
var msgHash = '';
var signatureBuffer = '';
try {
msgBuffer = ethUtil.toBuffer(msg);
msgHash = ethUtil.hashPersonalMessage(msgBuffer);
signatureBuffer = ethUtil.toBuffer(signature);
} catch (e) {
return res.status(500).json({ step: 1, message: 'Security error! Request with invalid params!' })
}
const signatureParams = ethUtil.fromRpcSig(signatureBuffer);
const publicKey = ethUtil.ecrecover(msgHash, signatureParams.v, signatureParams.r, signatureParams.s);
const adddressBuffer = ethUtil.publicToAddress(publicKey);
const address = ethUtil.bufferToHex(adddressBuffer);
console.log(`recvered address is ${address}`);
const isMatched = account.toLowerCase() == address.toLowerCase();
if (!isMatched) {
return res.status(500).json({ step: 0, message: 'Security error! Transaction caller is not signer!' })
}
console.log(`I will burn BSCFloki from ${account}`)
try {
var tx = await signedBscContract.burn(account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig)
console.log(tx.hash);
console.log("First transaction successed(BTK burned)");
} catch (e) {
console.log("Transaction to burn ETHFloki faild");
return res.status(500).json({ step: 1, message: 'Transaction Faild! \nCheck your account and token balance.' })
}
console.log(`I will mint ETHFloki to ${account}`)
try {
var tx1 = await signedEthContract.mint(account, account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig);
console.log(tx1.hash);
console.log("Second transaction successed(ETHFloki minted)");
} catch (e) {
console.log("Transaction to mint ETHFloki faild");
return res.status(500).json({ step: 2, message: 'Transaction Faild! \nCheck your account.' })
}
return res.send(`Success! \n ${amount} BSCFloki converted to ${amount} ETHFloki in your wallet.`);
});
module.exports = router;
这个api是签署客户的请求并使用签名的钱包及其私钥发送交易。
我目前正在使用 0.2x.x 版本的 Web3 JavaScript API。我通过在 solidity 中创建智能合约(在 REMIX IDE 上)部署了我的自定义 ERC20 代币。 我安装了 MetaMask 并在 https://wallet.ethereum.org/ 上进行了测试,以将一些自定义 ERC 令牌发送到我的另一个帐户。它运作良好。 我想使用 Web3js 在我的 JavaScript 代码中添加 'send custom ERC20 Token' 函数。
下面是我的代码。
var http = require('http');
var Web3 = require('web3');
var Tx = require('ethereumjs-tx');
var abi = [{"...."}];
var data = '0x00..';
var contract_addr = '0x00..';
var owner = '0x00..';
var web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/"));
web3.eth.getBlock("latest", (error, result) => {
//console.log('error:', error);
//console.log('results', result);
});
var myContract = web3.eth.contract(abi);
var myContractInstance = myContract.at(address);
eb3.eth.getDefaultAccount = owner;
var defaultAccount = web3.eth.getDefaultAccount;
console.log('DefaultAccount => ', defaultAccount);
var total = myContractInstance.totalSupply();
var balance = myContractInstance.balanceOf(defaultAccount);
console.log('DefulatAccount total => ',total);
console.log('DefaultAccount balance => ',balance);
var to = '0x00..';
var isAddress = web3.isAddress(to);
console.log('isAddress(to) => ',isAddress);
console.log('balanceOf(to) => ',myContractInstance.balanceOf(to));
我正在研究 'Ropsten Testnet',我发送了一些 'ether' 用于使用 sendRawTransaction()
进行测试。但我想做的只是发送我的自定义 ERC20 令牌。不是以太。如何在此脚本中发送我自己的令牌?我不知道。我尝试像下面那样使用 sendTransaction()
。
var rawTx = {
from : defaultAccount,
nonce : nonceHex,
gasPrice : gasPriceHex,
gas : gasHex,
to : to,
value : 100,
data : data
};
web3.eth.sendTransaction(rawTx, function(err, transactionHash) {
if (!err)
console.log('transactionHash => ',transactionHash);
else
console.log(err);
});
我不断收到错误。
Error: Invalid JSON RPC response: ""
at Object.InvalidResponse (/Users/rachel/dev/test/node_modules/web3/lib/web3/errors.js:38:16)
at XMLHttpRequest.request.onreadystatechange (/Users/rachel/dev/test/node_modules/web3/lib/web3/httpprovider.js:119:24)
at XMLHttpRequestEventTarget.dispatchEvent (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:64:18)
at XMLHttpRequest._setReadyState (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:354:12)
at XMLHttpRequest._onHttpResponseEnd (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:509:12)
at IncomingMessage.<anonymous> (/Users/rachel/dev/test/node_modules/xhr2/lib/xhr2.js:469:24)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1056:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
实在想不明白这有什么问题。请让我知道一些方法来处理这个问题。谢谢!
那么你可以尝试这样的事情-:
`myContractInstance.transfer(to,amount, {gas:estimateGas,
gasPrice:gasPrice,from:fromAddr, nonce: nonceis}, function(err,txhash)
{
console.log("error: "+err);
console.log("txhash: "+txhash);
}
`
这里'to'是你要发送代币的地址
'amount' 您要发送的代币数量
'estimateGas' 交易执行的 gas limit
'gasPrice' 每个 gas 单位的价格 wei
'fromAddr' 您要发送这些令牌的地址。由于交易将使用此地址签名,因此这应该是在 metamask 中选择的地址或传递给 infura
'nonceis' 交易的随机数
以EOS代币转账为例
下面的代码需要 web3
和 ethereumjs-tx
。如果您还没有安装它们,请使用 npm install web3 ethereumjs-tx
var Tx = require('ethereumjs-tx');
var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545/'))
// set token source, destination and amount
var myAddress = "0xaa597b7e8aaffe9f2a187bedb472ef3455957560"
var toAddress = "0xa013927bffe9e879134061b9330a01884a65497c"
var amount = web3.utils.toHex(1e16)
// get transaction count, later will used as nonce
web3.eth.getTransactionCount(myAddress).then(function(v){console.log(v); count = v})
// set your private key here, we'll sign the transaction below
var privateKey = new Buffer('6d...', 'hex')
// Get abi array here https://etherscan.io/address/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0#code
var abiArray = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name_","type":"bytes32"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"symbol_","type":"bytes32"}],"payable":false,"type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]
// Here you may get the abicode from a string or a file, here is a string case
// var abiArray = JSON.parse('[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"name_","type":"bytes32"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"symbol_","type":"bytes32"}],"payable":false,"type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]', 'utf-8')
var contractAddress = '0x86Fa049857E0209aa7D9e616F7eb3b3B78ECfdb0'
var contract = new web3.eth.Contract(abiArray, contractAddress, {from: myAddress})
var rawTransaction = {"from":myAddress, "gasPrice":web3.utils.toHex(2 * 1e9),"gasLimit":web3.utils.toHex(210000),"to":contractAddress,"value":"0x0","data":contract.methods.transfer(toAddress, amount).encodeABI(),"nonce":web3.utils.toHex(count)}
var transaction = new Tx(rawTransaction)
transaction.sign(privateKey)
web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'))
// check the balance
contract.methods.balanceOf(myAddress).call().then(function(balance){console.log(balance)})
您还可以使用 https://etherscan.io 查看交易。
交易发送后,您可以使用https://etherscan.io/address/0xaa597b7e8aaffe9f2a187bedb472ef3455957560#tokentxns进行查看,您会在Transactions
字段中看到如下待处理交易,但在[=15]中看不到=] 字段.
稍等1分钟,Transactions
字段和Token Transfers
字段都可以看到成功
错误:无效 JSON RPC 响应:“”表示 web3 无法连接到 rpc 节点。 尝试 运行 一个 ::
-测试网
geth --testnet --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpccorsdomain '*' --rpcapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --ws --wsaddr 127.0.0.1 --wsport 8546 --wsorigins '*' --wsapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --maxpeers 25 0 --gasprice 100
-主网
geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpccorsdomain '*' --rpcapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --ws --wsaddr 127.0.0.1 --wsport 8546 --wsorigins '*' --wsapi personal,admin,db,eth,net,web3,miner,shh,txpool,debug --maxpeers 25 0 --gasprice 100
现在在你的情况下(使用 infura testnet 节点),从 infura.io 获取一个 id。 应该是这样的::
https://ropsten.infura.io/id
您需要调用ERC20合约的transfer方法。 例如:
myContractInstance.methods.transfer(toAddress, amount);
并且在您的原始交易对象中:
- 您不需要金额
- 地址应该是合约地址(不是方法调用中的toAddress)
例如:
var rawTx = {
from : defaultAccount,
nonce : nonceHex,
gasPrice : gasPriceHex,
gas : gasHex,
to : <contractAddress>,
data : data
};
我们只需要 'ethers' 就可以做到。 在这种情况下,我将后端 api 与节点 js 一起使用。
const fs = require('fs');
const express = require('express');
const router = express.Router();
const { ethers, BigNumber } = require('ethers');
const ethUtil = require('ethereumjs-util');
const BridgeEth = require("../../client/src/components/contract/EthBridge.json");
const BridgeBsc = require("../../client/src/components/contract/BridgeBsc.json");
const addresses = require("../../client/src/components/contract/config.json");
const eth_bridge = addresses.eth_bridge;
const bsc_bridge = addresses.bsc_bridge;
const net_config = require("../../net.config.json");
const ethnet = net_config.eth_url;
const bscnet = net_config.bsc_url;
const eth_provider = new ethers.providers.JsonRpcProvider(ethnet);
const bsc_provider = new ethers.providers.JsonRpcProvider(bscnet);
const eth_contract = new ethers.Contract(eth_bridge, BridgeEth, eth_provider);
const bsc_contract = new ethers.Contract(bsc_bridge, BridgeBsc, bsc_provider);
var privateKey = fs.readFileSync('./secret', 'utf-8');
const adminaccount = {
publicKey: net_config.admin_public_key,
privateKey: privateKey
}
console.log(`Admin account is ${adminaccount.publicKey}`);
const adminEthWallet = new ethers.Wallet(adminaccount.privateKey, eth_provider);
const adminBscWallet = new ethers.Wallet(adminaccount.privateKey, bsc_provider);
const signedEthContract = eth_contract.connect(adminEthWallet);
const signedBscContract = bsc_contract.connect(adminBscWallet);
const sig = "0x6162636400000000000000000000000000000000000000000000000000000000";
router.post('/eth2bsc', async (req, res) => {
var account = req.body.account;
var amount = req.body.amount;
var signature = req.body.signature;
var msg = req.body.hash;
const isValidAddress = ethUtil.isValidAddress(account);
const isValidAmount = amount > 0;
const isValidSign = ethUtil.isHexPrefixed(String(signature));
const isValidHash = ethUtil.isHexPrefixed(String(msg));
if (!(isValidAddress && isValidAmount && isValidSign && isValidHash)) {
return res.status(500).json({ step: 1, message: `Security error! Server received invalid prams!${isValidAddress}, ${amount}, ${isValidSign}, ${isValidHash}` });
}
var msgBuffer = '';
var msgHash = '';
var signatureBuffer = '';
try {
msgBuffer = ethUtil.toBuffer(msg);
msgHash = ethUtil.hashPersonalMessage(msgBuffer);
signatureBuffer = ethUtil.toBuffer(signature);
} catch (e) {
return res.status(500).json({ step: 1, message: 'Security error! Request with invalid params!' })
}
const signatureParams = ethUtil.fromRpcSig(signatureBuffer);
const publicKey = ethUtil.ecrecover(msgHash, signatureParams.v, signatureParams.r, signatureParams.s);
const adddressBuffer = ethUtil.publicToAddress(publicKey);
const address = ethUtil.bufferToHex(adddressBuffer);
console.log(`recvered address is ${address}`);
const isMatched = account.toLowerCase() == address.toLowerCase();
if (!isMatched) {
return res.status(500).json({ step: 1, message: 'Security error! Transaction caller is not signer!' })
}
console.log(`I will burn BSCFloki from ${account}`)
try {
var tx = await signedEthContract.burn(account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig)
console.log(tx.hash);
console.log("First transaction successed(ETHFloki burned)");
} catch (e) {
console.log("Transaction to burn ETHFloki faild");
return res.status(500).json({ step: 1, message: 'Transaction Faild! \nCheck your account and token balance.' })
}
console.log(`I will mint BSCFloki to ${account}`)
try {
var tx1 = await signedBscContract.mint(account, account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig);
console.log(tx1.hash);
console.log("Second transaction successed(BSCFloki minted)");
} catch (e) {
console.log("Transanction to mint BSCFloki faild");
return res.status(500).json({ step: 2, message: 'Transaction Faild! \nCheck your account.' })
}
return res.send(`Success! \n ${amount} ETHFloki converted to ${amount} BSCFloki in your wallet.`);
});
router.post('/bsc2eth', async (req, res) => {
var account = req.body.account;
var amount = req.body.amount;
var signature = req.body.signature;
var msg = req.body.hash;
const isValidAddress = ethUtil.isValidAddress(account);
const isValidAmount = amount > 0;
const isValidSign = ethUtil.isHexPrefixed(String(signature));
const isValidHash = ethUtil.isHexPrefixed(String(msg));
if (!(isValidAddress && isValidAmount && isValidSign && isValidHash)) {
return res.status(500).json({ step: 1, message: 'Security error! Server received invalid prams!' });
}
var msgBuffer = '';
var msgHash = '';
var signatureBuffer = '';
try {
msgBuffer = ethUtil.toBuffer(msg);
msgHash = ethUtil.hashPersonalMessage(msgBuffer);
signatureBuffer = ethUtil.toBuffer(signature);
} catch (e) {
return res.status(500).json({ step: 1, message: 'Security error! Request with invalid params!' })
}
const signatureParams = ethUtil.fromRpcSig(signatureBuffer);
const publicKey = ethUtil.ecrecover(msgHash, signatureParams.v, signatureParams.r, signatureParams.s);
const adddressBuffer = ethUtil.publicToAddress(publicKey);
const address = ethUtil.bufferToHex(adddressBuffer);
console.log(`recvered address is ${address}`);
const isMatched = account.toLowerCase() == address.toLowerCase();
if (!isMatched) {
return res.status(500).json({ step: 0, message: 'Security error! Transaction caller is not signer!' })
}
console.log(`I will burn BSCFloki from ${account}`)
try {
var tx = await signedBscContract.burn(account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig)
console.log(tx.hash);
console.log("First transaction successed(BTK burned)");
} catch (e) {
console.log("Transaction to burn ETHFloki faild");
return res.status(500).json({ step: 1, message: 'Transaction Faild! \nCheck your account and token balance.' })
}
console.log(`I will mint ETHFloki to ${account}`)
try {
var tx1 = await signedEthContract.mint(account, account, BigNumber.from(String(amount * Math.pow(10, 18))), 0, sig);
console.log(tx1.hash);
console.log("Second transaction successed(ETHFloki minted)");
} catch (e) {
console.log("Transaction to mint ETHFloki faild");
return res.status(500).json({ step: 2, message: 'Transaction Faild! \nCheck your account.' })
}
return res.send(`Success! \n ${amount} BSCFloki converted to ${amount} ETHFloki in your wallet.`);
});
module.exports = router;
这个api是签署客户的请求并使用签名的钱包及其私钥发送交易。