Solidity: Is it possible to combine event emit and require? - blockchain

If a user doesn't send enough enough Eth, I'd like the UI to know and respond with a message.
This function validates msg.value, but I'd like to trigger and event (which the UI can respond to) in this case.
function doSomething() external payable {
require(
msg.value == price,
'Please send the correct amount of ETH'
);
//Do something
}
Is this the correct way to do that?
Is there any way to combine require() with sending events?
function doSomething() external payable {
if (msg.value < amount){
emit NotEnoughEth('Please make sure to send correct amount');
revert();
}
//Do something
}

emit NotEnoughEth('Please make sure to send correct amount');
you can't do this.
to be able to emit a types.Log you need your evm.Call() to execute without doing a revert. There are 2 instructions in the EVM you are referring to: makeLog (https://github.com/ethereum/go-ethereum/blob/2aaff0ad76991be8851ae30454d2e2e967704102/core/vm/instructions.go#L828) this is the one that creates and Event log. And opRevert (https://github.com/ethereum/go-ethereum/blob/2aaff0ad76991be8851ae30454d2e2e967704102/core/vm/instructions.go#L806) , so if you do the revert, your Call() will return an error, and all the result of the transaction on ethereum State database will be reverted and nothing will be saved. Since the storage is cancelled, there is no way for your Log to be saved on the blockchain.
This is the code that will check for error, and reverts to previously saved state (aka Snapshot) :
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
gas = 0
}
// TODO: consider clearing up unused snapshots:
//} else {
// evm.StateDB.DiscardSnapshot(snapshot)
}
return ret, gas, err
}
https://github.com/ethereum/go-ethereum/blob/2aaff0ad76991be8851ae30454d2e2e967704102/core/vm/evm.go#L280
Even though the opRevert() instruction doesn't explicitly returns an error, the Jump table is configured to always return an error for opRevert :
instructionSet[REVERT] = &operation{
execute: opRevert,
dynamicGas: gasRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
reverts: true,
returns: true,
}
https://github.com/ethereum/go-ethereum/blob/2aaff0ad76991be8851ae30454d2e2e967704102/core/vm/jump_table.go#L155
and the Interpreter will issue errExecutionReverted on its own:
case operation.reverts:
return res, ErrExecutionReverted
https://github.com/ethereum/go-ethereum/blob/2aaff0ad76991be8851ae30454d2e2e967704102/core/vm/interpreter.go#L297

Related

How payable function in solidity receives money by msg.value method?

I have two payable functions in 2 different contracts. One is "BuyLand" and the other is "depositEthers". There are some checks in both functions but I am facing a confusion that when I call a payable function with some value in value field, then is it necessary that function will receive that Ethers either checks or conditions inside the function are true or not?
Confusion with my functions:
BuyLand function receives ethers either checks are true or not.
depositEthers function receives ethers only when checks are true but not when checks are false.
How is it possible that 2 payable functions behave differently?
// 1st Function
function depositEthers() public payable
{
require(users[msg.sender].flag != 0, "You are not a registered user,
get yourself registered first");
require(msg.value > 0, "No Ethers was sent, Please send Ethers");
users[msg.sender].balance += msg.value;
}
// 2nd Function
function BuyLand
(
uint _landId
) public payable
{
require(landOwnerMapping[_landId] != msg.sender, "You can not Buy
Land because you are the Owner");
require(BuyerMapping[msg.sender].isVerified == true, "Buyer is not
verified");
require(SellerMapping[landOwnerMapping[_landId]].isVerified == true,
"Seller is not verified");
require(Lands[_landId].isVerified == true, "Land is not verified");
if (msg.value > Lands[_landId].LandPrice*1000000000000000000)
{
//payable(msg.sender).transfer(address(this).balance);
emit buyingLand("Land not bought, sent more Ethers than Land
price",
_landId, Lands[_landId].LandPrice, landOwnerMapping[_landId],
msg.sender);
}
else if (msg.value < Lands[_landId].LandPrice*1000000000000000000)
{
//payable(msg.sender).transfer(address(this).balance);
emit buyingLand("Land not bought, sent less Ethers than Land
price",
_landId, Lands[_landId].LandPrice, landOwnerMapping[_landId],
msg.sender);
}
else
{
payable(landOwnerMapping[_landId]).transfer(msg.value);
landOwnerMapping[_landId] = msg.sender;
ownerMapping[msg.sender] = _landId;
emit buyingLand("Land bought successfully",
_landId, Lands[_landId].LandPrice, landOwnerMapping[_landId],
msg.sender);
}
}
When you define a function with the payable keyword, this means that the function expects to receive a value in terms of Wei. You will notice after deploying your contract that this adds a parameter to your method. The value passed can be any amount of Ether, even zero, and this is where your require statement comes into play.
When you call the payable() function inside a method like in BuyLand, this allows the contract to send Ether to the address specified in the first parameter, or in your case landOwnerMapping[_landId].
Basically it's the difference between using payable as a keyword, and using it as a method. You can find out more about it in the solidity documents.

Set ETH price in Solidty

I made a basic smart contract:
contract Coursetro {
uint counter = 0;
event SetCounter(uint value);
function setCounter(uint value) public {
counter +=1;
emit SetCounter(value);
}
function getCounter() public view returns (uint) {
return counter;
}
}
but i don't know how to set a fixed ETH price for setCounter function
for exemple how can i set the price of 1 ETH to run setCounter function?
So i could just take the 1 ETH and put it on my wallet, as a sale.
First of all, welcome to the community. I recommend adding your code to your post directly, so that it is easier to read.
You could use the 'payable' modifier to make the function able to receive ether. Then you can simply make a require statement to check if the value sent was enough. Something like this:
function setCounter(uint value) public payable {
require(msg.value >= 1 ether, "Error msg here");
// You can additionally return the surplus ether.
if (msg.value > 1) {
payable(msg.sender).transfer(msg.value - 1 ether);
}
// Now send ether to your wallet.
payable("your wallet address").transfer(1 ether);
// Rest of the logic.
counter +=1;
emit SetCounter(value);
}
Hope it is useful :)

Smarthome AOG. The best way (in 2021) to integrate state of devices with firestore fields

I have this project: https://github.com/neuberfran/firebasefunction/blob/main/firebase/functions/smart-home/fulfillment.js
It works well. But, for example, I want to implement a condition that if I have the garage closed and I said "close garage", the Home assistantt will alert me about it.
As shown in the photo below, I am using an rpi3/iot-device/back-end that controls the garagestate field.
I need to know the best way to implement this condition, that is, read the value of the garagestate field and from that, know if I can open the garage or not:
You'd probably need to add an intermediary condition in your onExecute to return an error based on the Firestore state:
// ...
for (const target of command.devices) {
const configRef = firestore.doc(`device-configs/${target.id}`)
const targetDoc = await configRef.get()
const {garagestate} = targetDoc.data()
if (garagestate === false) {
// garagestate exists and is false
// return an error
return {
requestId,
payload: {
status: 'ERROR',
errorCode: 'alreadyClosed'
}
}
}
// ...
}
// ...

Sign and broadcast smart contract transaction in TRON network

Using a node.js client, I'm trying to invoke a smart contract function by:
Creating a transaction
Signing it
Broadcast the signed transaction
I deployed a simple smart contract:
pragma solidity ^0.4.25;
contract Test {
event MyEvent(address sender, string message, uint sessionId, uint value);
event TestEvent(address sender);
constructor() public {
}
function testFunction(string message, uint sessionId)
public
payable {
emit MyEvent(msg.sender, message, sessionId, msg.value);
}
function test2()
public {
emit TestEvent(msg.sender);
}
}
I'm having success invoking test2 (when sending an empty parameters array) but failing to invoke testFunction which takes parameters.
I've tried using tronweb triggerSmartContract call:
async function triggerSmartContract() {
try {
const options = {
feeLimit: 1000000000,
callValue: 50
};
const parameters = [{type: 'string', value: 'test-round-id-1'}, {type: 'uint', value: 12345}];
const issuerAddress = tronWeb.defaultAddress.base58;
const functionSelector = 'testFunction(string, uint)';
let transactionObject = await tronWeb.transactionBuilder.triggerSmartContract (
contract_address,
functionSelector,
options,
parameters,
tronWeb.address.toHex(issuerAddress)
);
if (!transactionObject.result || !transactionObject.result.result)
return console.error('Unknown error: ' + txJson, null, 2);
// Signing the transaction
const signedTransaction = await tronWeb.trx.sign(transactionObject.transaction);
if (!signedTransaction.signature) {
return console.error('Transaction was not signed properly');
}
// Broadcasting the transaction
const broadcast = await tronWeb.trx.sendRawTransaction(signedTransaction);
console.log(`broadcast: ${broadcast}`);
} catch (e) {
return console.error(e);
}
}
The code runs and creates a transaction on the blockchain but with result of FAIL-REVERT OPCODE EXECUTED.
The wallet of the address and key that are defined in tronweb object has enough funds in it (it's not out of gas/not enough funds matter).
Also the function in the smart contract is defined as payable.
Since I can invoke successfully any function with no parameters I tend to think the problem relates to the way I construct the parameters array. I built the parameters array this way (pairs of type and value) after going over the source code of the transactionBuilder (line 833).
Any suggestions?
Had exactly the same issue. Solution:
Remove spaces in const functionSelector = 'testFunction(string,uint)'; as malysh advised
Change uint to uint256
try writing function parameters without spaces.const functionSelector = 'testFunction(string,uint)'; I read in the documentation that you have to write without spaces, it worked for me on python.

Some Problems of Indy 10 IdHTTP Implementation

In regard to Indy 10 of IdHTTP, many things have been running perfectly, but there are a few things that don't work so well here. That is why, once again, I need your help.
Download button has been running perfectly. I'm using the following code :
void __fastcall TForm1::DownloadClick(TObject *Sender)
{
MyFile = SaveDialog->FileName;
TFileStream* Fist = new TFileStream(MyFile, fmCreate | fmShareDenyNone);
Download->Enabled = false;
Urlz = Edit1->Text;
Url->Caption = Urlz;
try
{
IdHTTP->Get(Edit1->Text, Fist);
IdHTTP->Connected();
IdHTTP->Response->ResponseCode = 200;
IdHTTP->ReadTimeout = 70000;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReuseSocket;
Fist->Position = 0;
}
__finally
{
delete Fist;
Form1->Updated();
}
}
However, a "Cancel Resume" button is still can't resume interrupted downloads. Meant, it is always sending back the entire file every time I call Get() though I've used IdHTTP->Request->Ranges property.
I use the following code:
void __fastcall TForm1::CancelResumeClick(TObject *Sender)
{
MyFile = SaveDialog->FileName;;
TFileStream* TFist = new TFileStream(MyFile, fmCreate | fmShareDenyNone);
if (IdHTTP->Connected() == true)
{
IdHTTP->Disconnect();
CancelResume->Caption = "RESUME";
IdHTTP->Response->AcceptRanges = "Bytes";
}
else
{
try {
CancelResume->Caption = "CANCEL";
// IdHTTP->Request->Ranges == "0-100";
// IdHTTP->Request->Range = Format("bytes=%d-",ARRAYOFCONST((TFist->Position)));
IdHTTP->Request->Ranges->Add()->StartPos = TFist->Position;
IdHTTP->Get(Edit1->Text, TFist);
IdHTTP->Request->Referer = Edit1->Text;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReadTimeout = 70000;
}
__finally {
delete TFist;
}
}
Meanwhile, by using the FormatBytes function, found here, has been able to shows only the size of download files. But still unable to determine the speed of download or transfer speed.
I'm using the following code:
void __fastcall TForm1::IdHTTPWork(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
__int64 Romeo = 0;
Romeo = IdHTTP->Response->ContentStream->Position;
// Romeo = AWorkCount;
Download->Caption = FormatBytes(Romeo) + " (" + IntToStr(Romeo) + " Bytes)";
ForSpeed->Caption = FormatBytes(Romeo);
ProgressBar->Position = AWorkCount;
ProgressBar->Update();
Form1->Updated();
}
Please advise and give an example. Any help would sure be appreciated!
In your DownloadClick() method:
Calling Connected() is useless, since you don't do anything with the result. Nor is there any guarantee that the connection will remain connected, as the server could send a Connection: close response header. I don't see anything in your code that is asking for HTTP keep-alives. Let TIdHTTP manage the connection for you.
You are forcing the Response->ResponseCode to 200. Don't do that. Respect the response code that the server actually sent. The fact that no exception was raised means the response was successful whether it is 200 or 206.
You are reading the ReuseSocket property value and ignoring it.
There is no need to reset the Fist->Position property to 0 before closing the file.
Now, with that said, your CancelResumeClick() method has many issues.
You are using the fmCreate flag when opening the file. If the file already exists, you will overwrite it from scratch, thus TFist->Position will ALWAYS be 0. Use fmOpenReadWrite instead so an existing file will open as-is. And then you have to seek to the end of the file to provide the correct Position to the Ranges header.
You are relying on the socket's Connected() state to make decisions. DO NOT do that. The connection may be gone after the previous response, or may have timed out and been closed before the new request is made. The file can still be resumed either way. HTTP is stateless. It does not matter if the socket remains open between requests, or is closed in between. Every request is self-contained. Use information provided in the previous response to govern the next request. Not the socket state.
You are modifying the value of the Response->AcceptRanges property, instead of using the value provided by the previous response. The server tells you if the file supports resuming, so you have to remember that value, or query it before then attempting to resumed download.
When you actually call Get(), the server may or may not respect the requested Range, depending on whether the requested file supports byte ranges or not. If the server responds with a response code of 206, the requested range is accepted, and the server sends ONLY the requested bytes, so you need to APPEND them to your existing file. However, if the server response with a response code of 200, the server is sending the entire file from scratch, so you need to REPLACE your existing file with the new bytes. You are not taking that into account.
In your IdHTTPWork() method, in order to calculate the download/transfer speed, you have to keep track of how many bytes are actually being transferred in between each event firing. When the event is fired, save the current AWorkCount and tick count, and then the next time the event is fired, you can compare the new AWorkCount and current ticks to know how much time has elapsed and how many bytes were transferred. From those value, you can calculate the speed, and even the estimated time remaining.
As for your progress bar, you can't use AWorkCount alone to calculate a new position. That only works if you set the progress bar's Max to AWorkCountMax in the OnWorkBegin event, and that value is not always know before a download begins. You need to take into account the size of the file being downloaded, whether it is being downloaded fresh or being resumed, how many bytes are being requested during a resume, etc. So there is lot more work involved in displaying a progress bar for a HTTP download.
Now, to answer your two questions:
How to retrieve and save the download file to a disk by using its original name?
It is provided by the server in the filename parameter of the Content-Disposition header, and/or in the name parameter of the Content-Type header. If neither value is provided by the server, you can use the filename that is in the URL you are requesting. TIdHTTP has a URL property that provides the parsed version of the last requested URL.
However, since you are creating the file locally before sending your download request, you will have to create a local file using a temp filename, and then rename the local file after the download is complete. Otherwise, use TIdHTTP.Head() to determine the real filename (you can also use it to determine if resuming is supported) before creating the local file with that filename, then use TIdHTTP.Get() to download to that local file. Otherwise, download the file to memory using TMemoryStream instead of TFileStream, and then save with the desired filename when complete.
when I click http://get.videolan.org/vlc/2.2.1/win32/vlc-2.2.1-win32.exe then the server will process requests to its actual url. http://mirror.vodien.com/videolan/vlc/2.2.1/win32/vlc-2.2.1-win32.exe. The problem is that IdHTTP will not automatically grab through it.
That is because VideoLan is not using an HTTP redirect to send clients to the real URL (TIdHTTP supports HTTP redirects). VideoLan is using an HTML redirect instead (TIdHTTP does not support HTML redirects). When a webbrowser downloads the first URL, a 5 second countdown timer is displayed before the real download then begins. As such, you will have to manually detect that the server is sending you an HTML page instead of the real file (look at the TIdHTTP.Response.ContentType property for that), parse the HTML to determine the real URL, and then download it. This also means that you cannot download the first URL directly into your target local file, otherwise you will corrupt it, especially during a resume. You have to cache the server's response first, either to a temp file or to memory, so you can analyze it before deciding how to act on it. It also means you have to remember the real URL for resuming, you cannot resume the download using the original countdown URL.
Try something more like the following instead. It does not take into account for everything mentioned above (particularly speed/progress tracking, HTML redirects, etc), but should get you a little closer:
void __fastcall TForm1::DownloadClick(TObject *Sender)
{
Urlz = Edit1->Text;
Url->Caption = Urlz;
IdHTTP->Head(Urlz);
String FileName = IdHTTP->Response->RawHeaders->Params["Content-Disposition"]["filename"];
if (FileName.IsEmpty())
{
FileName = IdHTTP->Response->RawHeaders->Params["Content-Type"]["name"];
if (FileName.IsEmpty())
FileName = IdHTTP->URL->Document;
}
SaveDialog->FileName = FileName;
if (!SaveDialog->Execute()) return;
MyFile = SaveDialog->FileName;
TFileStream* Fist = new TFileStream(MyFile, fmCreate | fmShareDenyWrite);
try
{
try
{
Download->Enabled = false;
Resume->Enabled = false;
IdHTTP->Request->Clear();
//...
IdHTTP->ReadTimeout = 70000;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->Get(Urlz, Fist);
}
__finally
{
delete Fist;
Download->Enabled = true;
Updated();
}
}
catch (const EIdHTTPProtocolException &)
{
DeleteFile(MyFile);
throw;
}
}
void __fastcall TForm1::ResumeClick(TObject *Sender)
{
TFileStream* Fist = new TFileStream(MyFile, fmOpenReadWrite | fmShareDenyWrite);
try
{
Download->Enabled = false;
Resume->Enabled = false;
IdHTTP->Request->Clear();
//...
Fist->Seek(0, soEnd);
IdHTTP->Request->Ranges->Add()->StartPos = Fist->Position;
IdHTTP->Request->Referer = Edit1->Text;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReadTimeout = 70000;
IdHTTP->Get(Urlz, Fist);
}
__finally
{
delete Fist;
Download->Enabled = true;
Updated();
}
}
void __fastcall TForm1::IdHTTPHeadersAvailable(TObject*Sender, TIdHeaderList *AHeaders, bool &VContinue)
{
Resume->Enabled = ( ((IdHTTP->Response->ResponseCode == 200) || (IdHTTP->Response->ResponseCode == 206)) && TextIsSame(AHeaders->Values["Accept-Ranges"], "bytes") );
if ((IdHTTP->Response->ContentStream) && (IdHTTP->Request->Ranges->Count > 0) && (IdHTTP->Response->ResponseCode == 200))
IdHTTP->Response->ContentStream->Size = 0;
}
#Romeo:
Also, you can try a following function to determine the real download filename.
I've translated this to C++ based on the RRUZ'function. So far so good, I'm using it on my simple IdHTTP download program, too.
But, this translation result is of course still need value improvement input from Remy Lebeau, RRUZ, or any other master here.
String __fastcall GetRemoteFileName(const String URI)
{
String result;
try
{
TIdHTTP* HTTP = new TIdHTTP(NULL);
try
{
HTTP->Head(URI);
result = HTTP->Response->RawHeaders->Params["Content-Disposition"]["filename"];
if (result.IsEmpty())
{
result = HTTP->Response->RawHeaders->Params["Content-Type"]["name"];
if (result.IsEmpty())
result = HTTP->URL->Document;
}
}
__finally
{
delete HTTP;
}
}
catch(const Exception &ex)
{
ShowMessage(const_cast<Exception&>(ex).ToString());
}
return result;
}