How can i get a block info from a running ethereum node using geth or nodejs or any other language? For example to get a block data from bitcoin, there is a config file which run a blocknotify.sh file when a transaction is confirmed and in that blocknotify.sh file there is this command : bitcoin-cli getblock "$#" >> "$#.json" which gets the block data then i can send a post request of that block data to an api. So i want to do the same thing that is to get the block data from ETH nodes and send a post request to an api when a transaction is confirmed. How can i do this ?
While running in geth console, you can use the web3 library to get block information:
> var blockNumber;
undefined
> var blockInfo;
undefined
> web3.eth.getBlockNumber(function(e, r) { blockNumber = r; });
undefined
> blockNumber;
2515149
> web3.eth.getBlock(blockNumber, function(e, r) { blockInfo = r; });
undefined
> blockInfo
{ difficulty: 1319089762, extraData: "0xd58301070d8650617269747986312e32332e30826c69", gasLimit: 4700036, gasUsed: 0, hash: "0xf9c495b5e5bcd3935aa4a6fd5a43009de29ca7d7be77d4b7cc4c68b8704bc422", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x94cd009bbba97f30a36845e2025edf7544d62439", mixHash: "0xb8794a6d1b777f8c6fdf509f04896642f67dab82dc872ae9cbe9bcbf85172972", nonce: "0x264c013815697c2f", number: 2515149, parentHash: "0x0d2b4185aec34d8963b4c0b8fa7abb8604e1452bcb7f7f7d7a75a3d1cfd85f92", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 537, stateRoot: "0xc3810c2e763669a6e0219c78990c92b91c634d23810f4bd67144c4d76b9cfe6e", timestamp: 1516811780, totalDifficulty: 7642933628775356, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] }
The web3 documentation can be found here: v0.2x.x, v1.0
EDIT - Example transaction info retreival:
> web3.eth.getTransaction('0xaebaf7e8207c417f6bb7920f3820e5220738d6825b6f577e3c3d0736d3c95b49', function(e, r) { if (e) console.log(e); else res = r; });
undefined
> res
{ blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0x0a78c28257b40d5076ea180bc6a9e4c597c5ea98", gas: 90000, gasPrice: 40000000000, hash: "0xaebaf7e8207c417f6bb7920f3820e5220738d6825b6f577e3c3d0736d3c95b49", input: "0x", nonce: 32, r: "0x902f7a7b7c3e4ebcab47014f9c3b81858cdc4b90ef36d9630adaf30b47ec370f", s: "0x6f09e7d66dc2146afa108322f433c12b629eadd149e1e113669b6bd2e0cd467e", to: "0xb8ac544e818d01a9533c9556d572d8366b91d6c1", transactionIndex: 0, v: "0x2a", value: 200000 }
Related
I am working with the web3.js library on a small project that subscribes to events on a smart contract for an NFT to track all sales on that project through OpenSea. This is working fine when the sale is completed using ETH, but when a sale is made using another form of payment (USDC, DAI, WETH, etc) that OpenSea offers, the value on the transaction from web3.js is coming back as 0, and it shows 0 on etherscan.io as well. Here is an example transaction representing this issue: https://etherscan.io/tx/0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532. Any ideas on how to get the correct value, no matter what type of currency is used?
Ex of
web3.eth.getTransaction('0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532').then((response) => {
console.log(response);
});
Response
{
accessList: [],
blockHash: '0x6140d3cb5c271fb351e0a6e9e35b32cf0607ad526152f40f2d98107a97b0212b',
blockNumber: 13545512,
chainId: '0x1',
from: '0x3F4D7b0Eba8CB40D94713023d9Dc02FdB0a5169C',
gas: 391246,
gasPrice: '157998874325',
hash: '0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532',
input: '0xab834bab0000000000000000000000007be8076f4ea4a4ad08075c2508e481d6c946d12b00000000000000000000000074144fb8749f99382091118f7487f4a541be6d7700000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000bd3531da5cf5857e7cfaa92426877b022e612cf80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007be8076f4ea4a4ad08075c2508e481d6c946d12b0000000000000000000000003f4d7b0eba8cb40d94713023d9dc02fdb0a5169c00000000000000000000000074144fb8749f99382091118f7487f4a541be6d770000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd3531da5cf5857e7cfaa92426877b022e612cf80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165a0bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000618193b7000000000000000000000000000000000000000000000000000000006182e53b690c3f82a9b6570ef2de1af15e15942c931dba8a2bd9d5012b74b213beea4da400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165a0bc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006182d5b70000000000000000000000000000000000000000000000000000000000000000065ae400a094fafb63173dcf4c7861ca0264920136639dbe27b5956aa6282bb00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000007e0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c08da96edfaed45809c68db44cabb487dba56fd196fe46d02d679f0161dee94293eaedf214e043bc4d2e3dd69cce82e21ead967d7f1cd8fc505c04781e2d144e308da96edfaed45809c68db44cabb487dba56fd196fe46d02d679f0161dee94293eaedf214e043bc4d2e3dd69cce82e21ead967d7f1cd8fc505c04781e2d144e35c5321ae45550685308a405827575e3d6b4a84aa000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423b872dd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074144fb8749f99382091118f7487f4a541be6d7700000000000000000000000000000000000000000000000000000000000008ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423b872dd0000000000000000000000003f4d7b0eba8cb40d94713023d9dc02fdb0a5169c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: '201893464202',
maxPriorityFeePerGas: '1500000000',
nonce: 19,
r: '0x4c8e6fbe4a49439d957e0e725f3f7e897329557a972863143ff2a34458ab190',
s: '0x25afd4caa4cdc1f93923b5f41a89bc191d0e52c27a36e51e3015ed08560fea5d',
to: '0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b',
transactionIndex: 328,
type: 2,
v: '0x1',
value: '0'
}
You can get the transaction receipt containing event logs. In this case, you're looking for the Transfer event emitted by the ERC-20 contract, where the ERC-20 token sender is the same as the NFT receiver.
const run = async () => {
const receipt = await web3.eth.getTransactionReceipt('0x17f050e3fb6d8f0bbb4d9b4e8cd477f8197a87a3a68b360a60a028d7b1037532');
const nftReceiver = _getNFTReceiver(receipt.logs);
for (let log of receipt.logs) {
// keccak256 of "Transfer(address,address,uint256)"
if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
// ERC20 sender (1st param of the Transfer event) is the same as the NFT receiver
&& web3.eth.abi.decodeParameter('address', log.topics[1]).toLowerCase() === nftReceiver.toLowerCase()
// ERC-20 Transfer returns the value in `data`, while ERC-721 has the same signature but returns empty data
&& log.data !== '0x'
) {
console.log('ERC-20 token contract: ', log.address);
console.log('ERC-20 token amount (incl. decimals): ', web3.eth.abi.decodeParameter('uint256', log.data));
}
}
}
const _getNFTReceiver = (logs) => {
for (let log of logs) {
// keccak256 of "Transfer(address,address,uint256)"
if (log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
// ERC-20 Transfer returns the value in `data`, while ERC-721 has the same signature but returns empty data
&& log.data === '0x'
) {
// 2nd parameter of the `Transfer()` event contains the NFT receiver
return web3.eth.abi.decodeParameter('address', log.topics[2]);
}
}
return null;
}
run();
prints:
Token contract: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Token amount (incl. decimals): 6000000000
I'm using hardhat locally and have a react frontend up and running but I can't call the methods without errors.
I've tried both ethers.js and web3.
Here's my code and attempts. Please let me know if you see what I'm doing wrong.
I'm trying to interact with contracts that are deployed in the local hardhat env through web3
I'm unable to get back the data from the contract, here's the info
I have:
var list = await contract.methods.getList();
console.log("list ", list );
which gets me
list {arguments: Array(0), call: ƒ, send: ƒ, encodeABI: ƒ, estimateGas: ƒ, …}
When I do
var list = await contract.methods.getList().call();
console.log("list ", list );
I get this error in the browser:
Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.
I do:
Setup in console:
npx hardhat node
>Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
>Accounts
>========
>...
npx hardhat compile
> Nothing to compile
npx hardhat run scripts/deploy.js --network hardhat
Note: In the deploy.js file, I do a
const list = await contract.getList();
console.log("list", list ); // correctly outputs ["string", "string"]
The method:
mapping(uint256 => address) internal list;
uint256 internal listCount;
function getList() public override view returns (address[] memory) {
address[] memory assets = new address[](listCount);
for (uint256 i = 0; i < listCount; i++) {
assets[i] = list[i];
}
return assets;
}
In react App.js:
import Contract_from './data/abi/Contract_.json'; // Contract_ is a placer
var contract = new web3.eth.Contract(Contract_, address_given_on_deploy);
var contractAddress = await contract .options.address; // correctly outputs
var list= await contract.methods.getList().call();
console.log("list", list);
As you see, this doesn't return the values from the method. What am I doing wrong here?
For any reason and may be likely the issue, here's my config:
require("#nomiclabs/hardhat-waffle");
// openzeppelin adds
require("#nomiclabs/hardhat-ethers");
require('#openzeppelin/hardhat-upgrades');
//abi
require('hardhat-abi-exporter');
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async () => {
const accounts = await ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
networks: {
hardhat: {
gas: 12000000,
blockGasLimit: 0x1fffffffffffff,
allowUnlimitedContractSize: true,
timeout: 1800000,
chainId: 1337
}
},
solidity: {
compilers: [
{
version: "0.8.0",
settings: {
optimizer: {
enabled: true,
runs: 1000
}
}
},
{
version: "0.8.2",
settings: {
optimizer: {
enabled: true,
runs: 1000
}
}
},
],
},
abiExporter: {
path: './frontend/src/data/abi',
clear: true,
flat: true,
only: [],
spacing: 2
}
}
__
I thought maybe i would try ethers.js since that is what i do my testing in but same issue.
For whatever reason, I can "get" the contracts, print the methods that belong to them, but I can't actually call the methods.
Here's my ethers.js brevity:
provider = new ethers.providers.Web3Provider(window.ethereum);
if(provider != null){
const _contract = new ethers.Contract(address, _Contract, provider);
var list= await _contract.getList().call();
console.log("list", list);
}
The error i get from this is:
Error: call revert exception (method="getList()", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.4.0)
I've tried numerous contracts in the protocol and same thing for each
Im trying to integrate web3js with Trezor in a truffle dev network or using ropsten test network.
The idea is to sign the transactions using the hardware wallet and then send a raw transaction using web3js
Im getting that we dont have balance to make the transaction, probably because web3js isnt taking one of the 10 truffle accounts and is using the trezor address that isnt in my local network..
On ropsten i have some ethers and i get "invalid address"
Is there a way to send a signed transactions (with trezor) using web3js into a truffle develop network? i mean, is there a way to include the trezor address into the truffle network?
The situation in truffle is explained more in details here, but the question could be generalized to "is there a way to include hardware wallets into truffle development network?" : https://github.com/trufflesuite/truffle/issues/973
Using ropsten I have managed to send a transaction and receive a transaction hash in the callback, but if we query for that transaction we get that the transaction doesnt exists.. so.. how is that possible?
I tried deploying a contract into Ropsten too and now im getting "Invalid address" when invoking a smart contract function. Maybe the signing function is wrong? anyone could integrate Trezor transaction signining with web3js?
Do you guys see anything wrong in the signing and sending process that we have followed? Maybe is there something wrong on the R, V and S parameters handling
..
Another important thing is that i am using https://github.com/ethereumjs/ethereumjs-tx for creating the raw transactions
Issues published in web3js, truffle and trezzor connect with more information:
https://github.com/trufflesuite/truffle/issues/973
https://github.com/ethereum/web3.js/issues/1669
https://github.com/trezor/connect/issues/130
kind regards
trezorLogin = async()=> {
let trezor= await this.getTrezor();
// site icon, optional. at least 48x48px
let hosticon = 'https://doc.satoshilabs.com/trezor-apps/_images/copay_logo.png';
// server-side generated and randomized challenges
let challenge_hidden = '';
let challenge_visual = '';
//use anonimous functions on callback otherwise returns cross origin errors
trezor.requestLogin(hosticon, challenge_hidden, challenge_visual, function (result){
if (result.success) {
console.log('Public key:', result.public_key); // pubkey in hex
console.log('Signature:', result.signature); // signature in hex
console.log('Version 2:', result.version === 2); // version field
console.log(result);
}else {
console.error('Error:', result.error);
}
});}
trezorSignTx= async(transaction)=> {
let trezor= await this.getTrezor();
// spend one change output
let address_n = "m/44'/60'/0'/0/0"
// let address_n = [44 | 0x80000000,
// 60 | 0x80000000,
// 0 | 0x80000000 ,
// 0 ]; // same, in raw form
let nonce = transaction.nonce.substring(2); // note - it is hex, not number!!!
let gas_price = transaction.gasPrice.substring(2);
let gas_limit = transaction.gasLimit.substring(2);
let to = transaction.to.substring(2);
// let value = '01'; // in hexadecimal, in wei - this is 1 wei
let value = transaction.value.substring(2); // in hexadecimal, in wei - this is about 18 ETC
let data = transaction.data.substring(2); // some contract data
// let data = null // for no data
let chain_id = 5777; // 1 for ETH, 61 for ETC
return new Promise (function (resolve,reject) {
trezor.ethereumSignTx(
address_n,
nonce,
gas_price,
gas_limit,
to,
value,
data,
chain_id,
function (response) {
if (response.success) {
console.log('Signature V (recovery parameter):', response.v); // number
console.log('Signature R component:', response.r); // bytes
console.log('Signature S component:', response.s); // bytes
resolve(response);
} else {
console.error('Error:', response.error); // error message
resolve(null);
}
});
})
}
getTrezorAddress = async() => {
let trezor= await this.getTrezor();
// spend one change output
let address_n = "m/44'/60'/0'/0/0";
trezor.ethereumGetAddress(address_n, function (result) {
if (result.success) { // success
console.log('Address: ', result.address);
} else {
console.error('Error:', result.error); // error message
}
});
}
getTrezor = async() => {
let trezorC;
await getTrezorConnect
.then(trezorConnect => {
trezorC= trezorConnect;
})
.catch((error) => {
console.log(error)
})
return trezorC;
}
sendTransaction= async(address, amount, id)=>{
let tokenInstance = this.props.smartContractInstance;
let getData = tokenInstance.mint.getData(address, amount);
let tx = {
nonce: '0x00',
gasPrice: '0x09184e72a000',
gasLimit: '0x2710',
to: CONTRACT_ADDRESS,
value: '0x00',
from:CONTRACT_OWNER_ADDRESS,
data: getData
};
let response = await this.trezorSignTx(tx);
let web3;
let _this = this;
if (response!=null){
getWeb3
.then(results => {
web3= results.web3;
let v = response.v.toString();
if (v.length % 2 != 0){
v="0"+v;
}
tx.r=Buffer.from(response.r,'hex');
tx.v=Buffer.from(v,'hex');
tx.s=Buffer.from(response.s,'hex');
let ethtx = new ethereumjs(tx);
console.dir(ethtx.getSenderAddress().toString('hex'), );
const serializedTx = ethtx.serialize();
const rawTx = '0x' + serializedTx.toString('hex');
console.log(rawTx);
//finally pass this data parameter to send Transaction
web3.eth.sendRawTransaction(rawTx, function (error, result) {
if(!error){
_this.props.addTokens(id)
.then(()=>{
_this.setState({modalOpen: true});
_this.props.getAllTransactions();
}
);
}else{
alert(error)
}
});
})
.catch((error) => {
console.log(error)
})
}else{
alert("There was an error signing with trezor hardware wallet")
}
}
The getTrezorConnect function is just get window.trezorConnect asynchronously because the object is injected as script
<script src="https://connect.trezor.io/4/connect.js"></script>
let getTrezorConnect = new Promise(function(resolve, reject) {
// Wait for loading completion
window.addEventListener('load', function() {
let trezorConnect = window.TrezorConnect
return resolve(trezorConnect)
})});
export default getTrezorConnect
Well, after a lot of trying we have managed to send a raw transaction signed with Trezor to Ropsten, Truffle (see the edit on the bottom of the answer) and also to a local private Geth network, so, the code is ok and there is no problem with Trezor integration on those environments
https://ropsten.etherscan.io/address/0x89e2c46b22881f747797cf67310aad1a831d50b7
This are the things that i had changed in order to make it possible to send signed transactions to the Ropsten testnet.
This assumes that you have your contract deployed into Ropsten and you have the contract address.
1) Get the address of your Trezor account
getTrezorAddress = async() => {
let trezor= await this.getTrezor();
// spend one change output
let address_n = "m/44'/1'/0'/0/0";
trezor.ethereumGetAddress(address_n, function (result) {
if (result.success) { // success
console.log('Address: ', result.address);
} else {
console.error('Error:', result.error); // error message
}
});
}
2) Put the trezor address into the from field of your raw transaction, get the nonce of the transaction by getting the transaction count for that address. Important: use the "pending" optional parameter on getTransactionCount to get all the transactions of the account, otherwise you will be overriting pending transactions.
getNonce = async(address) => {
let web3 = await this.getWeb3();
return new Promise (function (resolve,reject) {
web3.eth.getTransactionCount(address, "pending", function (error,result){
console.log("Nonce "+result);
resolve(result);
});
});
}
let count = null;
await this.getNonce("0xedff546ac229317df81ef9e6cb3b67c0e6425fa7").then(result => {
if(result.length % 2 !==0){
result = "0"+result;
}
count = "0x"+result;
});
let tx = {
nonce: count ,
gasPrice: web3.toHex(gasPriceGwei*1e9),
gasLimit: web3.toHex(gasLimit),
to: CONTRACT_ADDRESS,
value: '0x00',
data: getData,
chainId:chainId,
from:"yourTrezzorAddress"
};
3) The r, s, v parameters were incorrect, the right way to handle them is take that values for the trezor response and just convert it to hexa:
// response is the Trezor sign response
tx.v= response.v;
tx.r="0x"+response.r;
tx.s="0x"+response.s;
let ethtx = new ethereumjs(tx);.
const serializedTx = ethtx.serialize();
const rawTx = '0x' + serializedTx.toString('hex');
//finally pass this data parameter to send Transaction
web3.eth.sendRawTransaction(rawTx, someCallbackFunction);
Important: the mining time in ropsten will be between 15 and 30 secs so if in your someCallbackFunction you check for the transaction receipt, using the hash, you will get null as result, because the transaction is in a pending state.
4) To test it at ropsten we use Infura, so we change the web3 provider:
import Web3 from 'web3'
import HDWalletProvider from "truffle-hdwallet-provider";
let getWeb3 = new Promise(function(resolve, reject) {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener('load', function() {
let results
let web3 = window.web3
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider.
web3 = new Web3(web3.currentProvider)
results = {
web3: web3
}
console.log('Injected web3 detected.');
return resolve(results)
} else {
// Fallback to localhost if no web3 injection. We've configured this to
// use the development console's port by default.
// let provider = new Web3.providers.HttpProvider("https://ropsten.infura.io/your_infura_api_key")
let mnemonic = "infura mnemonic"
let provider = new HDWalletProvider(mnemonic, "https://ropsten.infura.io/your_infura_api_key")
web3 = new Web3(provider)
results = {
web3: web3
}
console.log('No web3 instance injected, using Local web3.');
return resolve(results)
}
})
})
export default getWeb3
EDIT:
This also works on Truffle! check the last comments of this issue https://github.com/trufflesuite/truffle/issues/973
We developed a small library to use Truffle with Trezor hardware wallets: https://github.com/rarible/trezor-provider
It can be used like this:
const { createProvider } = require('#rarible/trezor-provider')
module.exports = {
networks: {
ropsten: {
provider: function() {
//websocket and http urls are supported
return createProvider({ url: "{infura or other}", path: "m/44'/60'/0'/0/0", chainId: 3 })
},
network_id: 3
}
}
};
It's easy to use web3js to call functions that don't require signing (e.g. functions that do not update the state of a contract). However, it's not clear how to call functions that require signing, other than manually unlocking my MetaMask wallet and calling functions inside Remix environment.
After deploying my dapp for the first time to Ropsten, I need to call createItem(string name, uint price) 100 times to populate the items array initially. Since I don't want to do it manually in Remix, I want to write a script that does it automatically.
It looks like I need to have ethereumjs-tx in addition to web3js to sign transactions programatically without having MetaMask. I also need to have my account and privateKey. With all this information and the official web3js doc, I come up with the following:
// Call an external function programatically
const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io"))
const account = "ACCOUNT_ADDRESS"
const privateKey = new Buffer('PRIVATE_KEY', 'hex')
const contract = new web3.eth.Contract(abi, CONTRACT_ADDRESS, {
from: account,
gas: 3000000,
})
const functionAbi = contract.methods.myFunctionName(myArgument).encodeABI()
let estimatedGas
contract.methods.myFunctionNAme(myArgument).estimateGas({
from: account,
}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16)
})
const txParams = {
gasPrice: '0x' + estimatedGas,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
}
const tx = new Tx(txParams)
tx.sign(privateKey)
const serializedTx = tx.serialize()
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).
on('receipt', console.log)
The code runs, but txParams is actually missing one key: nonce. When you run this, you get the following error:
Unhandled rejection Error: Returned error: nonce too low
Here are my questions:
Is this generally the right way to do what I am trying to do?
If 1 is true, how do I get the nonce parameter for a deployed contract?
References:
http://web3js.readthedocs.io/en/1.0/
https://github.com/ethereumjs/ethereumjs-tx
https://ethereum.stackexchange.com/questions/21402/web3-eth-call-how-can-i-set-data-param
https://ethereum.stackexchange.com/questions/6368/using-web3-to-sign-a-transaction-without-connecting-to-geth
UPDATE:
Thanks to Adam, now I learned how to get the nonce. So I added this following code:
let nonce
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16)
})
const txParams = {
gasPrice: '0x' + gasPrice,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
nonce: '0x' + nonce,
}
But now I keep running into this exception:
Unhandled rejection Error: Returned error: rlp: input string too long
for uint64, decoding into
(types.Transaction)(types.txdata).AccountNonce
Google search wasn't helpful except for letting me locate this file (https://github.com/ethereum/go-ethereum/blob/master/rlp/decode.go) that has the exception handler. Does anyone know how to solve this?
Generally looks correct. The only question I would have is how are you planning on loading the private key? You will either need to prompt for the private key, or import/read in the keystore and prompt for the password. You can use keythereum to accomplish this (See the key import section for example code).
The nonce is just an incremental number used to order transactions for an account. To get the correct value, simply use web3.eth.getTransactionCount(account)
EDIT - Example run using Ganache with locked accounts (--secure option):
SimpleContract.sol
pragma solidity ^0.4.19;
contract SimpleContract {
uint public val = 4;
function increment(uint amt) public {
val += amt;
}
function get() public constant returns (uint) {
return val;
}
}
test.js
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const provider = new Web3.providers.HttpProvider("http://localhost:8545");
const web3 = new Web3(provider);
const account = '0x9a6d82ef3912d5ab60473124bccd2f2a640769d7'; // Ganache
const privateKey = Buffer.from('70f1384b24df3d2cdaca7974552ec28f055812ca5e4da7a0ccd0ac0f8a4a9b00', 'hex');
const contractAddress = '0x6dd7c1c13df7594c27e0d191fd8cc21efbfc98b4'; // Deployed manually
const abi = [{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}];
const contract = new web3.eth.Contract(abi, contractAddress, {
from: account,
gasLimit: 3000000,
});
const contractFunction = contract.methods.increment(3);
const functionAbi = contractFunction.encodeABI();
let estimatedGas;
let nonce;
console.log("Getting gas estimate");
contractFunction.estimateGas({from: account}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: '0x09184e72a000',
gasLimit: 3000000,
to: contractAddress,
data: functionAbi,
from: account,
nonce: '0x' + nonce
};
const tx = new Tx(txParams);
tx.sign(privateKey);
const serializedTx = tx.serialize();
contract.methods.get().call().then(v => console.log("Value before increment: " + v));
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
contract.methods.get().call().then(v => console.log("Value after increment: " + v));
})
});
});
Results:
$ npm run my_test
> hello_world_dapp#1.0.0 my_test C:\cygwin\home\adamk\eth\hello_world_dapp
> node ./test.js
Getting gas estimate
Estimated gas: 6919
Nonce: 5
Value before increment: 19
{ transactionHash: '0xb6fdfc36cc32cb01a2be8832a387da586a44a37c1241bbf2979745536f206ed4',
transactionIndex: 0,
blockHash: '0xb6ee8d4e45585010d9a12d48aa37a478eb6021aad78599f1719cb424ab364bb5',
blockNumber: 10,
gasUsed: 26905,
cumulativeGasUsed: 26905,
contractAddress: null,
logs: [],
status: 1 }
Value after increment: 22
Here is a code-snippet for sending signed transaction on rinkeby.In a similar way,you can proceed for ropsten test network:
const Tx = require('ethereumjs-tx')
const Web3 = require('web3')
const web3 =new
Web3('https://rinkeby.infura.io/v3/9ce80a86c6c54d22aa821d0486a1a47d')
var account1 = '0xa00c70B72150D627cf53872eefD077079116B6a6'
var account2 = '0xD2a8aa318Fbc56995Db8C415BE6E40329DB1C56C'
const privateKey1 = Buffer.from(process.env.PRIVATE_KEY_1,'hex')
const privateKey2 = Buffer.from(process.env.PRIVATE_KEY_2,'hex')
web3.eth.getTransactionCount(account1,(err,txCount)=>{
const txObject = {
nonce:web3.utils.toHex(txCount),
to:account2,
value:web3.utils.toHex(web3.utils.toWei('0.1','ether')),
gasLimit:web3.utils.toHex(21000),
gasPrice:web3.utils.toHex(web3.utils.toWei('10','gwei')),
}
console.log(txObject)
const tx = new Tx(txObject)
tx.sign(privateKey1)
const serializedTransaction = tx.serialize()
const raw = '0x'+serializedTransaction.toString('hex')
web3.eth.sendSignedTransaction(raw,(err,txHash)=>{
console.log(txHash)
})
})
I was create a transaction, use the following code:
var Tx = require('ethereumjs-tx')
var rawTx = {
nonce: '0x100004',
gasPrice: '0x4a817c800', //0.00000002
gasLimit: '0x15f90', //90000
from: '0x1911c081b0a2359d77d1f858bf5b5b04a080f2bb',
to: '0x5f56bba9ee673d0c7bccf4be1a78b9db3a51114d',
value: '0x2386f26fc10000', // 0.01
data: '0x'
}
var tx = new Tx(rawTx)
tx.sign(privateKey)
var serializedTx = tx.serialize()
var signData = serializedTx.toString('hex');
console.log(signData)
output tx data
f86f831000048504a817c80083015f90945f56bba9ee673d0c7bccf4be1a78b9db3a51114d872386f26fc10000801ba08aacd5525b240df3149ead81c2fd4bf3d2c01cc81506a5442f727f92850c4935a07807e5a74e1994a4fce6727fa9422e34d5da83411a8065c14249e9b5845cac3e
then Broadcast Raw Transaction on http://testnet.etherscan.io/pushTx
show following message
{ "jsonrpc": "2.0", "id": 1, "result": "0x15b9ff6eca21cfe48e853458908abf53ccae131a9aeb4eb03d553281c77c1f89" }
```
but, I open the http://testnet.etherscan.io/tx/0x15b9ff6eca21cfe48e853458908abf53ccae131a9aeb4eb03d553281c77c1f89 , it can found, why?
In addition, I try call sendRawTransaction in the geth console with --testnet
eth.getTransaction(eth.sendRawTransaction('f86f831000048504a817c80083015f90945f56bba9ee673d0c7bccf4be1a78b9db3a51114d872386f26fc10000801ba08aacd5525b240df3149ead81c2fd4bf3d2c01cc81506a5442f727f92850c4935a07807e5a74e1994a4fce6727fa9422e34d5da83411a8065c14249e9b5845cac3e'))
return the info following
{
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000
",
blockNumber: null,
from: "0x1911c081b0a2359d77d1f858bf5b5b04a080f2bb",
gas: 90000,
gasPrice: 20000000000,
hash: "0x15b9ff6eca21cfe48e853458908abf53ccae131a9aeb4eb03d553281c77c1f89",
input: "0x",
nonce: 1048580,
to: "0x5f56bba9ee673d0c7bccf4be1a78b9db3a51114d",
transactionIndex: null,
value: 10000000000000000
}
All the operation in the test network, Data seems to be correct, but no effect, What did I missed?