Why does removing a SLOAD in constructor save so much gas? - solidity

I changed the state variable in the constructor to a numeric value and printed the difference in gas consumed. Here's my code. The results showed that constructing the BeforeDemo consumed 93915 gas, constructing the AfterDemo consumed 76288 gas and the difference was 17627. I thought the difference came from one SLOAD but one SLOAD can't cost so much gas. Why is there so much difference between the two?
contract BeforeDemo {
uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** 18);
constructor() {
check(INITIAL_SUPPLY);
}
function check(uint256 supply) public {
}
}
contract AfterDemo {
constructor() {
check(10000 * (10 ** 18));
}
function check(uint256 supply) public {
}
}

There is no SLOAD because constants are stored in the bytecode, not in storage. The difference in gas cos you see is due to the contract bytecode size.
When using uint256 public constant INITIAL_SUPPLY the compiler generates a public getter function for it:
function INITIAL_SUPPLY() public pure returns(uint256) { return 10000 * (10 ** 18) }
This means that more OPCODEs are run on contract deployment, resulting in a larger contract bytecode.
Contract deployment costs 32k gas + 200 per byte. On remix, I checked that BeforeDemo is 373 bytes long and AfterDemo is 284 bytes long.
(373 - 284) * 200 = 17800
So you get approx. 17800 gas difference on deployment because of the contract bytecode size.

Related

How to create a time-based upkeep directly from my contract rather than use the GUI?

I want to create a time-based upkeep directly from my contract. I was able to register and fund the upkeep but for some reason the function is not getting executed automatically.
Here's the code
`
// Goerli network
address public cronFactoryAddress = 0x1af3cE8de065774B0EC08942FC5779930d1A9622;
address public keeperRegistrar = 0x57A4a13b35d25EE78e084168aBaC5ad360252467;
constructor(){
cronFactory = ICronFactory(cronFactoryAddress);
}
function createUpkeep(string memory _cronString) public{
address _target = address(this);
bytes memory functionToCall = bytes(abi.encodeWithSignature("sendSalary(string)", _cronString));
bytes memory job = cronFactory.encodeCronJob(_target, functionToCall, _cronString);
uint256 maxJobs = cronFactory.s_maxJobs();
address delegateAddress = cronFactory.cronDelegateAddress();
address newCronUpkeep = address(new CronUpkeep(msg.sender, delegateAddress, maxJobs, job));
allUpkeeps.push(newCronUpkeep);
}
function fundUpkeep(uint256 _linkAmount, address _upkeepAddress) public{
bytes4 reg = bytes4(keccak256("register(string,bytes,address,uint32,address,bytes,bytes,uint96,address)"));
bytes memory _data = abi.encode(
"TestV2",
"",
_upkeepAddress,
uint32(500000),
address(this),
"",
"",
_linkAmount,
address(this)
);
bytes memory combinedData = abi.encodePacked(reg, _data);
LinkContract.transferAndCall(keeperRegistrar, _linkAmount, combinedData);
}
sendSalary is the function in my contract that I want to be executed at regular intervals.
cronFactory is the cron factory contract.
cronUpkeep is the cronUpkeep.sol contract from the chainlink github repo.
To create these functions, I created a time-based upkeep manually and used the transaction logs to find what all function are being called and implemented the same here.
But, Once I execute both these functions nothing happens, however, I am able to find the upkeep registered on chainlink's website . And also it shows the trigger as custom trigger on upkeep page on chainlink:
chanlink upkeep
Please let me know how I can solve this? Any help would be appreciated. Thanks in advance
Contracts cannot execute themselves. Function needs to be called. While contract (function) is not called, contract is sleeping, because every time it makes operations, they should be payed (aka gas), so there is no way to throw an allways-active-timer inside of the contract (infinite gas). It means that you have to make calls manually or use automation services like ChainLink, Openzepplin Defender etc.
You can make a requirement by time-passed with
uint256 private lastTimeStamp;
uint256 private interval;
constructor() {
lastTimeStamp = block.timestamp;
interval = 7 days;
}
function isTimePassed() public view returns (bool timePassed) {
timePassed = ((block.timestamp - lastTimeStamp) > /*7 days */ interval);
return timePassed;
}
function smth() public {
(bool timePassed) = isTimePassed();
...
}
Something like this.

Reentrancy attack with withdraw amount

I've been working on different ways to perform reentrancy attacks and there is one case which I have still not seen any working example on the internet. In the book Mastering Ethereum, the reentrancy attack is explained with a contract where the function withdraw(uint amount) takes the input amount. The version on Solidity has been updated a lot since then and whenever I try to perform a similar attack, it does not work. It works whenever the function withdraw() takes no arguments and it also works when using older versions.
Could anyone provide an example of a reentrancy attack where the target contract takes the withdraw amount as input?
Thank you!
Let's say you have 1 ether in the contract, and the contract has a total of 10 ether. You're trying to steal all 10 ether with re-entrancy, but that necessarily means the variable tracking your balance must underflow to the equivalent of uint256(-9) ether -- you're trying to withdraw 1 ether 10 times.. This will cause a revert in Solidity 0.8.0 or higher, since it has built in under/overflow protection. If you want it to work in 0.8.0, you have to wrap the balance reduction line with unchecked.
This code is still vulnerable to re-entrancy in 0.8.0, but only because it sets the balance to zero, and can't underflow
mapping(address => uint256) public balance;
function deposit() external payable {
balance[msg.sender] += msg.value;
}
function withdraw() external {
msg.sender.call{value: balance[msg.sender]}(""); // re-entrancy
balance[msg.sender] == 0; // cannot underflow
}
function withdrawV2(uint256 value) external {
require(value <= balance[msg.sender], "you don't have that much"); // not that this does anything...
msg.sender.call{value: balance[msg.sender]}("");
unchecked { // now it can underflow
balance[msg.sender] -= value;
}
}

Solidity: I need to reference bytes constants efficiently

I am implementing an on-chain font in Solidity. It works great, but I am not able to eliminate unnessary copies of bytes from code space (bytes constants) to the storage.
Here is the current implementation.
contract LondrinaSolid is IFontProvider {
uint constant fixed_height = 1024;
uint constant fixed_baseline = 805;
bytes constant font_0 = "\x4d\x60\xd1\x74\x05\x43\x8a\x56\x1f\x29\x56\x0b\xd2\x55\x21\x63\x50\x00\x00\x45\x62\x2e\x45\x4a\xc5\x45\xf4\x19\x45\xf5\x4b\x45\xf3\xb0\x45\xff\x65\x45\xfe\xeb\x55\x39\x2e\x56\x3b\x44\x55\x8b\x73\x65\x0c\x4c\x05\x73\xc0\x45\x02\xc5\x35\x97\x63\x50\x05\x95\x44\xfe\x21\x44\xb7\xcd\x03\x5a\x6d\x40\x75\x54\x06\x63\xfc\x54\x83\xe6\x54\x99\xc7\x54\x9a\xe0\x54\x01\xe3\x44\xf5\xd2\x44\xc9\xef\x44\xd5\xef\x44\x66\xef\x44\x66\x0b\x45\x72\x0b\x45\x3e\x40\x45\x40\x73\x50\x3d\x73\x55\x39\xf7\x05\x5a";
bytes constant font_1 = "\x4d\x50\xbc\x27\x06\x73\xf7\x54\xf4\xf5\x64\x27\xfe\x54\xc6\xfe\x54\xc6\x63\x50\x00\x00\x55\x4e\x0d\x55\x7c\x0c\x05\x73\x4e\x45\xfb\x4f\x45\xea\x05\x35\x8a\x08\x35\x61\x04\x35\xbd\xfe\x34\xaf\x63\x50\x00\x00\x45\xa1\xfa\x44\x7e\xfb\x04\x73\x9d\x54\x02\x99\x54\x01\x63\x50\x00\x00\x45\xbd\x64\x45\xac\x80\x05\x53\x09\x55\xfb\x0a\x65\x0d\x63\x50\x00\x00\x55\x1b\x14\x55\x4b\x1f\x05\x73\x46\x55\x0f\x4b\x55\x0f\x18\x45\xf9\x1c\x45\xed\x5a\x00";
bytes constant font_2 = "\x4d\x50\xd3\x57\x06\x73\x0a\x45\xa6\x18\x45\x93\x39\x45\xd6\x4b\x55\x0d\xfe\x54\x66\xcc\x54\x81\xb4\x54\x33\x7f\x54\x53\xb6\x54\x3b\xb2\x54\x41\xe8\x54\x24\xe3\x54\x4f\xf6\x54\xbb\xf5\x54\xc3\x63\x50\x00\x00\x55\x0f\x04\x55\x1e\x05\x05\x73\xbd\x45\xfb\xd4\x45\xfb\xee\x45\xfd\xf6\x45\xfd\x08\x45\xfa\x08\x45\xf7\xfd\x44\xb7\xff\x44\xab\xfd\x44\xcc\xf6\x44\xbc\x63\x50\x00\x00\x45\x04\x06\x35\xe9\x02\x55\x00\x00\x55\x23\xb5\x54\x48\x9f\x04\x73\x99\x45\xb4\xb9\x45\x90\x31\x45\xc5\x31\x45\x7f\xfc\x44\x56\xa4\x44\x14\xf2\x43\xe0\xc0\x53\x02\x53\x40\xf6\xbb\x55\x15\x58\x06\x63\x00\x55\x00\x39\x55\x15\xbe\x45\xff\x5a\x00";
...
bytes constant font_X = "\x4d\x50\x09\x35\x05\x73\x40\x55\x93\x4f\x55\xc7\x2f\x55\x94\x2e\x55\xad\xce\x54\xaa\xc8\x54\xbe\xcd\x54\xa5\xc9\x54\xad\x63\x50\x00\x00\x55\x28\x10\x55\x60\x0e\x05\x73\x50\x45\xfd\x50\x45\xfd\x63\x50\x00\x00\x55\x1e\xaf\x54\x25\x98\x04\x73\x1f\x45\x96\x1f\x45\x96\x63\x50\x00\x00\x55\x1c\x6a\x55\x20\x7a\x05\x73\x13\x55\x48\x19\x55\x51\x63\x50\x00\x00\x55\x30\x0e\x55\x5d\x09\x05\x73\x52\x55\x01\x57\x55\x02\x63\x50\x00\x00\x45\xe5\x9f\x44\xd8\x6e\x04\x73\xc5\x44\x3a\xc2\x44\x1d\x63\x50\x00\x00\x55\x34\x52\x54\x41\x29\x04\x73\x34\x45\x51\x34\x45\x51\x63\x50\x00\x00\x45\xb3\x01\x45\xa2\x01\x05\x73\xa1\x54\x03\x99\x54\x04\x63\x50\x00\x00\x45\xca\xc9\x45\xc5\xd0\x55\x00\x00\x45\xe3\xa5\x44\xe0\x93\x04\x73\xe8\x44\xa0\xe8\x44\xa0\x63\x50\x00\x00\x45\x76\xff\x44\x3b\x05\x05\x5a";
bytes constant font_Y = "\x4d\x50\x17\x30\x05\x73\x19\x55\x59\x30\x55\x8d\x57\x55\xdc\x57\x55\xe8\x63\x50\x00\x00\x45\xc5\x9e\x45\xbd\xb1\x05\x53\x12\x85\x1d\x10\x85\x22\x63\x50\x00\x00\x55\x42\x04\x55\x64\x03\x05\x73\x4a\x45\xfd\x58\x55\x01\x63\x50\x00\x00\x55\x3f\x56\x54\x55\x2e\x04\x53\xb6\x66\x15\xcb\x56\xe0\x73\x50\x39\x5f\x54\x3a\x51\x04\x63\x00\x55\x00\xb2\x44\xf2\x4c\x54\x07\x00\x55\x00\xbb\x54\xc7\xb9\x54\xcc\x00\x55\x00\xbe\x44\x48\xbc\x44\x34\x00\x55\x00\xeb\x44\xee\xab\x44\xf3\x73\x40\xc0\x08\x45\xa5\x07\x05\x5a";
bytes constant font_Z = "\x4d\x50\x0b\x33\x05\x73\x0a\x55\x33\x09\x55\x4f\x03\x55\x4b\x0d\x55\x54\x63\x50\x00\x00\x55\xeb\x06\x55\xfc\x0c\x55\x00\x00\x45\x8a\xa5\x45\x7a\xb9\x05\x53\x26\x75\x4a\x19\x75\x73\x63\x50\x00\x00\x55\x01\x44\x55\x01\x5c\x05\x73\x04\x55\x4e\x0c\x55\x58\x63\x50\x00\x00\x55\xab\xf8\x54\xcf\xf9\x04\x73\xdb\x55\x03\x06\x56\x07\x63\x50\x00\x00\x55\x0e\x6b\x54\x02\x46\x54\x00\x00\x45\x0c\xff\x34\xf6\x08\x55\x00\x00\x55\x92\x2d\x54\xa4\x14\x04\x73\x6a\x45\x64\x6d\x45\x59\x01\x45\xad\x02\x45\x9d\xff\x44\xb5\xfc\x44\xb0\x63\x50\x00\x00\x45\x35\xfd\x44\x12\xfc\x04\x73\x3f\x54\x01\xf7\x53\x09\x5a\x00";
mapping(uint => bytes) fontData;
mapping(uint => uint) widths;
constructor() {
registerAll();
}
function _register(string memory _char, bytes memory _bytecode, uint _width) internal {
uint key = uint(uint8(bytes(_char)[0]));
if (_bytecode.length > 0) {
fontData[key] = _bytecode;
}
widths[key] = _width;
}
function registerAll() external onlyOwner {
_register("0", font_0, 554);
_register("1", font_1, 414);
_register("2", font_2, 540);
...
_register("X", font_X, 531);
_register("Y", font_Y, 531);
_register("Z", font_Z, 531);
}
function height() external pure override returns(uint) {
return fixed_height;
}
function baseline() external pure override returns(uint) {
return fixed_baseline;
}
function widthOf(string memory _char) external view override returns(uint) {
uint key = uint(uint8(bytes(_char)[0]));
return widths[key];
}
function pathOf(string memory _char) external view override returns(bytes memory) {
uint key = uint(uint8(bytes(_char)[0]));
return fontData[key];
}
}
The problem is in the _register method, which stores the mapping between the charactor and the vector data in the "fontData". Even thought the font data itself is already in this smart contract, it copies it to the "memory" space when calling this method and copies it into the "storage", which is quite expensive (12M units of gas).
One possible work around is to eliminate the _register method, and implement a binary search in pathOf method to directly return font_X, but that code is ugly and not scalable.
Ideally, I just want to store the references to those font data in "fontData" and return them in pathOf method.
I'd appreciate any suggestions, including writing some assembly solution (which I am comfortable with).
I was able to solve this problem by creating a function for each font, and having a mapping from character codes to those functions in the storage.
With this change, I was able to reduce the deployment cost of one particular font from 0.13ETH to 0.027ETH (assuming 30gwei gas price).

What will happen if gas used out when invoke outside smart contract

contract Counter {
uint256 value = 0;
function addOne() external {
value += 1;
}
}
contract MyContract {
Counter a = .....;
Counter b = .....;
// .......
function myFunc() external {
// ......
a.addOne();
b.addOne();
}
}
What will happen if gas uses out while on executing b.addOne()?
If a.value will add one?
How can I avoid it?
Gas is the currency used to incentivize miners to execute OPCODEs on the EVM. Each transaction on Ethereum is ATOMic, meaning every OPCODE in the transaction must run otherwise the entire transaction is rejected. So if you run out of gas, your transaction will simply revert.
You can avoid this by using a gas estimator (e.g. Remix has this integrate out of the box) or doing it by hand (since every OPCODE's gas requirement is known, and so is the gas price, even if it does fluctuate moment to moment).
Then when you make the call, you specify how much gas to use eg: a.addOne{gas: 69696969 }()
If you send too much, the unspent gas will be returned to the caller.

Exceeds block gas limit when deploy the smart contract local node

I have been working on Smart Contract development with the local node running. It worked two weeks ago, and I got an error when I tried to deploy the same contract to the same local node. The error exceeds block gas limit. I have a high gas in the truffle.js, but still, I am getting the same error. Why is that?
pragma solidity ^0.4.8;
contract Verifier {
bool public isSignedW = false;
address public theAddress;
function recoverAddr(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) returns (address) {
theAddress= ecrecover(msgHash, v, r, s);
return theAddress;
}
function isSigned(address _addr, bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) returns (bool) {
isSignedW= ecrecover(msgHash, v, r, s) == _addr;
return isSignedW;
}
}
It is difficult to say when we can't see your Smart Contract and how you deploy it. Maybe can you provide more information ?
Can we have the information of the block in which your contract is mined ?
web3.eth.getBlock(BLOCK_NUMBER, function(error, result){
if(!error)
console.log(JSON.stringify(result));
else
console.error(error);
})
One possibility is the gas limit became too low to be able to mine your contract. Because the gas limit in a private network decrease with each mined block to the limit of 4.7 Million gas, if you use the default parameters.