Is there a way to get a specific event for a contract? - ethereum

I'm going to run a contract in Ethereum, Binance, Polygon, and Clayton.
Is there a best practice for polling events that occur in a contract?
I want to get the event by requesting, not by subscribing to it.
If we use EtherScan api, are all sequences decisively guaranteed?
The goal is to send requests periodically and store all the specific events that occurred in the contract in db.
I'd appreciate your help.

When you query the raw blockchain data, each event log contains logIndex, which is the position of the event within the block.
Example with web3js:
const pastEventLogs = await web3.eth.getPastLogs({
fromBlock: 16168375,
toBlock: 16168375,
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT on Ethereum
topics: [
web3.utils.keccak256("Transfer(address,address,uint256)")
]
});
returns
...
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
blockHash: '0x026c2f62c8ad51b1e28c5fe6885c6025b2c4145aef08b65feb10b6e20dd2c0bc',
blockNumber: 16168375,
data: '0x0000000000000000000000000000000000000000000000000000000001618bd0',
logIndex: 732,
...
Authors of 3rd party APIs (e.g. Etherscan) can chose to order their results according to the logIndex but they can also chose different ordering.

Related

Can I attach a piece of text to a transaction record?

According to the docs for web3.eth.sendTransaction and the docs for eth_sendTransaction:
The transaction object can contain an optional data parameter which should be a String that consists of:
either an ABI byte string containing the data of the function call on a contract, or in the case of a contract-creation transaction the initialisation code.
I want to assign a string to data and have the string be stored along with the record of the transaction on the blockchain, so that I can retrieve that string when I retrieve the record of that transaction later.
const [testAccount] = await window.ethereum.request({ method: "eth_requestAccounts" })
const web3 = new Web3(window.ethereum)
if (!testAccount) {
return
}
let transactionHash = await web3.eth.sendTransaction({
from: testAccount,
to: testAccount,
value: web3.utils.toWei('0.0003'),
data: web3.utils.utf8ToHex(JSON.stringify({ a: 1, b: 2 }))
})
let transaction = await web3.eth.getTransaction(transactionHash)
let data = JSON.parse(web3.utils.hexToUtf8(transaction.data))
console.log(data.a) // should log 1
When I execute sendTransaction (using Metamask, while connected to the Ropsten network), I get the following error:
Error: Error: TxGasUtil - Trying to call a function on a non-contract address
{
"originalError": {
"errorKey": "transactionErrorNoContract",
"getCodeResponse": "0x"
}
}
Apparently, you cannot assign any string to data and expect the string to be incorporated in the record of the transaction on the blockchain, out-of-the-box, simply by assigning a value to it. Is this correct?
Question: Do I need to write a custom smart-contract in order to achieve this ?
This is a feature/limitation specific to MetaMask. Possibly to protect their users who want to interact with a smart contract but are connected to a different network where the contract is not deployed.
However, it is technically possible to send a valid transaction with non-empty data field to a non-contract address. You just need to use a different node provider to broadcast the transaction. Unfortunately the node provider in MetaMask is hardcoded so it's not possible using this wallet.
Example: This transaction on the Ropsten testnet to the 0xdac1... address that has the USDT token contract deployed on the mainnet, but is a non-contract address on the testnet. It is a valid transaction, successfully bought gas from the sender address to cover the transaction fees, mined in a block, just didn't execute any smart contract code (as there is no smart contract on the recipient address).
Do I need to write a custom smart-contract in order to achieve this ?
You can also write a smart contract function in Solidity that receives data as a function argument, but does nothing on this. Thus, GoEthereum node stores this data as calldata of the transaction and it can be later retrieved.
I am pretty sure some cross-chain bridges operate in this manner. Transactions only write data as the part of calldata (cheaper than Solidity storage) and then other clients read it from there.

How does an incoming request to a nodejs server get handled when the event loop is waiting for a DB operation

I have a route in my API, as an example lets call it /users/:userId/updateBalance. This route will fetch the users current balance, add whatever comes from the request and then update the balance with the newly calculated balance. A request like this comes into the server for a specific user every 30 minutes, so until recently, I thought a concurrency issue to be impossible.
What ended up happening is that somewhere, a sent request failed and was only sent again 30 minutes later, about within a second of the other request. The result was that, as I can see it in the database, both of these requests fetched the same balance from the DB and both added their respective amounts. Essentially, the second request actually read a stale balance, as normally it should execute after request 1 has executed.
To give a numerical example for some more clarity, lets say request 1 was to add $2 to the balance, and request 2 was to add $5, and the user had a balance of $10. If the requests act in parallel, the users balance would end at either $12 or $15 depending on whether request 1 or request 2 finished first respectively, because both requests fetch a balance of $10 from the DB. However, obviously the expected behaviour is that we want request 1 to execute, update the users balance to $12, and then request 2 to execute and update the balance from $12 to $17.
To give some better perspective of the overall execution of this process: the request is received, a function is called, the function has to wait for the balance from the DB, the function then calculates the new balance and updates the db, after which execution is completed.
So I have a few questions on this. The first being, how does node handle incoming requests when it is waiting for an asynchronous request like a MySQL database read. Given the results I have observed, I assume that when the first request is waiting for the DB, the second request can commence being processed? Otherwise I am uncertain of how such asynchronous behaviour is experienced within a single threaded environment like node.
Secondly, how do I go about controlling this and preventing it. I had wanted to use a MySQL transaction with a forUpdate lock, but it turns out it seems not possible due to the way the code is currently written. Is there a way to tell node that a certain block of code can not be executed "in parallel"? Or any other alternatives?
You are right, while node waits for the database query to return, it will handle any incoming requests and start that requests database call before the first one finishes.
The easiest way to prevent this IMO would be to use queues. Instead of processing the balance update directly in the route handler, that route handler could push an event to a queue (in Redis, in AWS SQS, in RabbitMQ etc) and somewhere else in your app (or even in a completely different service) you would have a consumer that listens to new events in that queue. If an update fails, add it back to the beginning of the queue, add some wait time, and then try again.
This way, no matter how many times your first request fails, your balance will be correct, and pending changes to that balance will be in the correct order. In case of an event in the queue failing repeatedly, you could even send an email or a notification to someone to have a look at it, and while the problem is fixed pending changes to the balance will be added to the queue, and once it's fixed, everything will be processed correctly.
You could even read that queue and display information to your user, for instance tell the user the balance has pending updates so it might not be accurate.
Hope this helps!
The first being, how does node handle incoming requests when it is waiting for an asynchronous request like a MySQL database read
The event loop of nodejs makes this happens, otherwise you'll have a totally sync programm with super-low performances.
Every single async function invocked in a context will be executed after the context itself has been executed.
Between the finish of execution of the context and the execution of the async function, other async functions can be scheduled for been executed (this "insertion" is managed by the event loop).
If an async function is awaited, the remaining code of the context is scheduled somewhere after the execution of the async function.
Is more clear when playing with it. Example 1:
// Expected result: 1, 3, 4, 2
function asyncFunction(x) {
// setTimeout as example of async operation
setTimeout(() => console.log(x), 10)
}
function context() {
console.log(1)
asyncFunction(2)
console.log(3)
}
context()
console.log(4)
Example 2:
// Expected result: 1, 2, 3
function asyncFunction(x) {
// Promise as example of async operation
return new Promise((resolve) => {
console.log(x)
resolve()
})
}
async function context() {
console.log(1)
await asyncFunction(2)
console.log(3)
}
context()
Example 3 (more similar to your situation):
// Expected result: 1, 2, 4, 5, 3, 6
function asyncFunction(x) {
// Promise as example of async operation
return new Promise((resolve) => {
console.log(x)
resolve()
})
}
async function context(a, b, c) {
console.log(a)
await asyncFunction(b)
console.log(c)
}
context(1, 2, 3)
context(4, 5, 6)
In your example:
when the server receive a connection, the execution of the handler is scheduled
when the handler is executed, it schedule the execution of the query, and the remaining portion of the handler context is scheduled after that
In between scheduled executions everything can happen.

Web3.js: Get return value from contract method execution

I have ERC-20 token smart-contract which methods I call using sendSignedTransaction from web3.js. After I know transaction is succesfully mined I need to check contract method execution result. How do I do it if all I have is transaction hash?
Example: method transferFrom(from, to, tokens) returns true or false depending on whether transferring was successful. So if I try to transfer 100 tokens from empty wallet, contract method will return false.
Upd: Okay, as I understood there is no way of determining method outcome using txHash after transaction is mined and confirmed. Then which ways exists to handle this case? How can I make sure that tokens were transferred?
You can emit an event inside your contract code ( actually in ERC20 standard there is always a transfer event present ) and then inside web3js, read all the events till latest block using this line of code:
Events = Contract.eventName({}, {fromBlock: 0, toBlock: 'latest'});

eth.estimateGas fails with contract address as second parameter

Newbie.
There is a go-ethereum method:
eth.estimateGas({from:'firstAccount', to:'secondAccount'})
that works well,
but same method with contract address like:
eth.estimateGas({from:'firstAccount', to:'contractAddr'})
fails with error
gas required exceeds allowance or always failing transaction
I have looked into go-ethereum source code and it has the line, that contains proposal to use contract address as second parameter:
https://github.com/ethereum/go-ethereum/blob/master/accounts/abi/bind/base.go#L221
The question is: is there any possibily to use eth.estimateGas with contract address as second parameter and how to avoid above error?
Thank you.
You're not specifying what you're executing in the contract, so there's nothing to estimate. When you estimateGas for a transfer to an EOA account, there is no contract code to execute, so there is no message data to be sent as part of the transaction object. If you're estimating gas on a contract call, you need to include the data for the contract.
For example, if you want to estimate gas to setValue(2) method in this contract
pragma solidity ^0.4.19;
contract SimpleContract {
uint256 _value;
function setValue(uint256 value) public {
_value = value;
}
}
your call would be
var data = '552410770000000000000000000000000000000000000000000000000000000000000002';
eth.estimateGas({from: fromAccount, to: contractAddress, data});
The value for data comes from encoding the function signature and the parameter value(s). You can use a simple tool (like https://abi.hashex.org) to generate this. You just enter the function name along with the parameter argument types and their values, and it will generate the message data for you. You can also do this using web3js.
EDIT - I neglected to consider contracts with fallback functions. Executing estimateGas on a contract without passing in message data provide the estimate for contracts that have a fallback function. If the contract does not have a fallback function, the call will fail.

How to send messages on a transaction in the Ethereum

I want to send a message on a transaction. Here is my codes:
_data = web3.toHex('xxxx');
instance.function_name(param1, param2, param3, param4, {value: web3.toWei(_price, 'ether'), from: web3.eth.accounts[0], data:_data}).then(...);
The transaction is processed successfully, But the input data message is not the _data value in the etherscan.io
can anybody help me? Thank you.
The data field in the transaction object is used when deploying a contract or when using the general sendTransaction or sendRawTransaction methods. If you are using a contract instance, the data field is ignored.
From the Solidity docs:
Object - (optional) The (previous) last parameter can be a transaction object, see web3.eth.sendTransaction parameter 1 for more. Note: data and to properties will not be taken into account.
If you want to send the data manually, use sendTransaction.
The information shown in Etherscan is the decoded data from the signed transaction describing the function call made. It is not free form user data (if that's what you're trying to insert). The first 32 bits of the data are the function signature and each 256 bit block afterwards are the parameters.
See this source for more in-depth information.