In solidity what do numbers initiate to assuming we just call a plain number.
uint256 plainNumber
I understand it's zero. But what I'm asking for, is there a way to detect if that number has been set by the compiler or a user variable. For example...
uint256 plainNumber;
***some code and we continue***
plainNumber = 0;
Is there any way to detect if I set the plainNumber or if it was simply initialized at zero? Any special protocol?
From my experience, you cannot know if the value is 0 because it's the inital state or if it is 0 because of 'you'.
One 'solution' would be to use a struct of this kind, and set the isSet bool when you change the plainNumber value :
struct myPlainNumber {
uint256 plainNumber
bool isSet
}
Related
My doubt is from the below code:
contract RandomNumber{
uint number;
function get_random() public{
bytes32 ramdonNumber = keccak256(abi.encodePacked(block.timestamp,blockhash(block.number-1)));
number = uint(ramdonNumber);
}
}
We assign a random number to the variable number but if I don't set number public or create another public function to retrieve the value then nobody would know the exactly value through Etherscan. But what about the miners? Can they retrieve these unrevealed data in some ways?
I have tried:
Google, Ethereum whitepaper, Solidity documentation
Assuming that the contract is deployed on a public network (e.g. Ethereum), then the value is always readable.
Not directly through the autogenerated getter function (that's available only for public properties), which means it's not available onchain.
But anyone (including miners) can create an offchain app (for example in JavaScript) that queries the specific storage slot where the value is stored - in this case it's in slot number 0. And it returns the "secret" value.
JS code using ethers library, a wrapper to RPC API of Ethereum nodes:
const number = await provider.getStorageAt(CONTRACT_ADDRESS, SLOT_NUMBER);
Docs: https://docs.ethers.org/v5/api/providers/provider/#Provider-getStorageAt
And the actual RPC API method: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getstorageat
In my smart contract, I have a function that does something (not relevant), but there are two ways I could potentially accept arguments to the function: using a struct, or by encoding the values and simply passing a bytes value like so:
// Only 3 fields for simplicity, but in theory, there could be any arbitrary num. of fields
struct Data {
address user;
uint256 amount;
address receiver;
}
function executeSomething(Data memory data) external returns(bool) {
address user = data.user;
_;
return true;
}
vs
function executeSomething(bytes memory data) external returns(bool) {
address user = (data >> (bytes.length - 160)); // since address is of 160 bytes
_;
return true;
}
In case 1, I'm simply passing a struct, whereas in the second case, I'm passing an argument encoded as bytes memory for which I can extract the argument values by using bitshift tricks.
My question is this:
What are the potential gas savings of these?
Is it more recommended to use a struct (ease of use + sanity purposes)?
Since bytes memory is not of fixed length like bytes32, what is the tipping point in which using a struct makes better sense (gas-savings-wise) than using bytes memory.
I have this code in my solidity contract:
uint256 constant maxNum = 10000;
function mintNewFull(uint16 tokenId) public {
require (0 <= tokenId && tokenId < maxNum;
// do other stuff
}
And I called it using this code in ethers which worked:
contractWithSigner.mintNewFull(3);
But then later when I changed it to trying to mint with tokenID 11:
contractWithSigner.mintNewFull(11);
It didn't work. And I tried and every number under 10 seems to work and numbers greater than that don't.
Is it some uint16 uint256 problem? Should my constant maxNum be changed to uint16, is it impossible to call using ethers a function with a uint16 parameter? I have no idea how to pass in a uimt16 instead of a uint256 because I couldn't find how to declare parameter types in the ethers docs. It seems like everyone just uses numbers or strings so that's confusing (especially when I will later have to pass in an array.)
I figured it out!!! Turns out it was something in the
// do other stuff
part where I was causing an integer overflow.
Yayyy I'm so happy :):)
As it is now, anyone can call the setMyString function in the FirstContract. I'm trying to restrict access to that function to an instance of SecondContract. But not one specific instance, any contract of type SecondContract should be able to call setMyString.
contract FirstContract{
String public myString;
function setMyString(String memory what) public {
myString=what;
}
}
contract SecondContract{
address owner;
address firstAddress;
FirstContract firstContract;
constructor(address _1st){
owner=msg.sender;
firstAddress=_1st;
firstContract=FirstContract(firstAddress);
}
function callFirst(String memory what){
require(msg.sender==owner);
firstContract.setMyString("hello");
}
}
Solidity currently doesn't have an easy way to validate an address against an interface.
You can check the bytecode, whether it contains the specified signatures (of the public properties and methods). This requires a bit larger scope than a usual StackOverflow answer, so I'm just going to describe the steps instead of writing the code.
First, define the desired list of signatures (1st 4 bytes of keccak256 hash of the name and arguments datatypes) that you're going to be looking for. You can find more info about signatures in my other answers here and here.
An example in the documentation shows how to get any address's (in your case msg.sender) bytecode as bytes (dynamic-length array).
You'll then need to loop through the returned bytes array and search for the 4-byte signatures.
If you find them all, it means that msg.sender "implements the interface". If any of the signatures is missing in the external contract, it means it doesn't implement the interface.
But... I'd really recommend you to rethink your approach to whitelisting. Yes, you'll need to maintain the list and call setIsSecondContract() when a new SecondContract wants to call the setMyString() function for the first time. But it's more gas efficient for all callers of the FirstContract's setMyString() function, as well as easier to write and test the functionality in the first place.
contract FirstContract{
String public myString;
address owner;
mapping (address => bool) isSecondContract;
modifier onlySecondContract {
require(isSecondContract[msg.sender]);
_;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function setIsSecondContract(address _address, bool _value) public onlyOwner {
isSecondContract[_address] = _value;
}
function setMyString(String memory what) public onlySecondContract {
myString=what;
}
}
I have a problem about handle function value.
likes the title ..
I already know contract's function call other contract's function depend on:
addr.call(abi.encodeWithSignature("function(parameter_type)", parameter ));
but what if I want to handle the function's value(ex:bool) temporarily (memory) for condition .
I had seen abi.encodePacked() , but I don't even know what parameter feedback to me (compile error , different parameter type), that I can't even storage it .
Some articles write it only for bytes, uint , but I only want to do condition(bool).
You don't need to do that .call unless you want to preserve the msg.sender.
If the msg.sender does not matter for you, I recommend you to look at interfaces. Now, what you have to do, is to create an interface with the functionB definition of that you want to call, and then call it.
Example:
(some deployed contract)
contract MySecondContract {
function functionB() public returns(bool) {
return true;
}
}
(get the deployed contract address and use it in your code like)
interface IMySecondContract {
function functionB() external returns(bool);
}
contract MyFirstContract {
function functionA() public returns(bool) {
bool result = IMySecondContract(MySecondContract-address-here).functionB();
return result;
}
}
If you do want to keep using .call, here you can see that the .call method returns two values.
Something like
(bool success, bytes myReturn) = addr.call(...);
and then this, might work.
Something else that might help is this new way of debugging.