Please check this code:
contract Token is StandardToken {
function transfer(address _to, uint256 _value) public returns (bool success) {
return super.transfer(_to, _value);
}
}
contract CrowdSale {
token = Token(:address)
function buyToken() payable {
token.transfer(beneficiary,tokenAmount); // OPERATION A
anotherAddress.transfer(msg.value); // OPERATION B
}
}
In the above example, will OPERATION A and OPERATION B be called in same block?
Or buyToken will be called which calls token.transfer and wait till it is mined and then anotherAddress.transfer is called in next block once first is mined?
I think you are mistaken about some concepts here. A block registers transactions.
The transaction is the fact of calling a method, deploying a contract, "moving the state of a contract".
In simple words : yes, a contract called by another contract will be called in the same block as the block won't contain the instructions itself but the result of the transaction .
In order to be able to give the result of a transaction, by logic, the whole instructions need to be processed on same block, otherwise the miner couldn't determine if the transaction is valid and then register it into the block.
Related
I have a ERC20 smart contract with edited transfer function
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
if(_transactionMaxValue > 0){
require(_transactionMaxValue >= amount, "You can not transfer more than 1000000 tokens at once!");
}
transfer(recipient, amount);
}
I have added if statement in transfer function, in case user indicates limit per single transaction before deploying. I was wondering if this would affect gas fees when transferring tokens, in order to decide weather to leave the "universal" ERC20 smart contract template with indictable transaction limit or compose new one, with no if statement, if no transaction limit was indicated.
I was wondering if this would affect gas fees when transferring tokens
Adding the (if and require) conditions increases the total amount of gas used, but the increase is small in the context of gas usage of other operations of the parent transfer() function. It's because in the overriding function, you're performing "just" one more storage read and few other operations in memory.
Execution of your transfer() function costs 54,620 gas (including the parent function; assuming the require() condition doesn't fail). While execution of just the parent transfer() function costs 52,320 gas.
You need to use super.transfer(recipient, amount); if you want to invoke the parent transfer() function. Without the super keyword, you'd lock the script in an infinite recursion, always invoking itself.
Also, in order to correctly override the parent function, you need to state the override modifier (and virtual if you are planning to override this function as well) before the public visibility modifier, and to return the value as declared.
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
uint256 _transactionMaxValue = 1000000;
constructor() ERC20("MyToken", "MyT") {
_mint(msg.sender, 1000000);
}
// moved the `override virtual` before the `public` modifier
function transfer(address recipient, uint256 amount) override virtual public returns (bool) {
if(_transactionMaxValue > 0){
require(_transactionMaxValue >= amount, "You can not transfer more than 1000000 tokens at once!");
}
// added `return super.`
return super.transfer(recipient, amount);
}
}
Everything #Petr Hejda suggested is correct.
Additional improvement here would be to make a require statement fail reason string smaller and more precise, as it will result in the smaller bytecode, and therefore cheaper deployment.
I am writing a pull payment function and, as with any external call it is good practice validate the result and check successful execution. I am using the following interface:
interface IAaveLendingPool {
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
function withdraw(
address asset,
uint256 amount,
address to
) external;
function getReservesList() external view returns (address[] memory);
}
aAveLendingPool= IAaveLendingPool(0x0543958349aAve_pooladdress)
The interface provided by Aave doesn't specify a return either. The withdraw function of the LendingPool.sol contract returns external override whenNotPaused returns (uint256).
The question: Can I use the returned uint to validate successful execution or aAveLendingPool.withdraw() returns a boolean? Will the following work as expected?
///#notice assigns dai to caller
require( aaveLendingPool.withdraw(
address(dai),
amount,
msg.sender), "Error, contract does not have enough DAI")
Please provide a solidity docs link to external function call return value, if any.
According to the Consensys Diligence Aave V2 audit:
ERC20 implementations are not always consistent. Some implementations of transfer and transferFrom could return ‘false’ on failure instead of reverting. It is safer to wrap such calls into require() statements to these failures.
Therefore, wrapping the transfer call in a require check is safe and sufficient.
require( aaveLendingPool.withdraw(
address(dai),
amount,
msg.sender), "Error, contract does not have enough DAI")
I came across this article dated 2019/9 about avoiding using solidity's transfer()/send(). Here is the reasoning from the article:
It looks like EIP 1884 is headed our way in the Istanbul hard fork. This change increases the gas cost of the SLOAD operation and therefore breaks some existing smart contracts.
Those contracts will break because their fallback functions used to consume less than 2300 gas, and they’ll now consume more. Why is 2300 gas significant? It’s the amount of gas a contract’s fallback function receives if it’s called via Solidity’s transfer() or send() methods. 1
Since its introduction, transfer() has typically been recommended by the security community because it helps guard against reentrancy attacks. This guidance made sense under the assumption that gas costs wouldn’t change, but that assumption turned out to be incorrect. We now recommend that transfer() and send() be avoided.
In remix, there is a warning message about the code below:
(bool success, ) = recipient.call{value:_amount, gas: _gas}("");
Warning:
Low level calls: Use of "call": should be avoided whenever possible. It can lead to unexpected behavior if return value is not handled properly. Please use Direct Calls via specifying the called contract's interface. more
I am not an expert on gas cost over execution of smart contract and security. So I post this article and would appreciate thoughts and comments about it.
First, it's good to know about the fallback function in Solidity:
It has no name, no arguments, no return value, and it can be defined as one per contract, but the most important feature is it is called when a non-existent function is called on the contract such as to send or transfer or call.value()(""). So if you want to send Ether directly to an address which is a contract address, the fallback function of the destination contract will be called.
If the contract's fallback function not marked payable, it will throw an exception if the contract receives plain ether without data.
Now let's look at the reentrancy attack
contract VulnerableContract {
mapping(address => uint) public balances;
function deposit() public payable {
require(msg.value > 1);
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public {
require(balances[msg.sender] >= _amount, "Not enough balance!");
msg.sender.call.value(_amount)("");
balances[msg.sender] -= _amount;
}
function getBalance() view public returns(uint) {
return address(this).balance;
}
fallback() payable external {}
}
VuinerableContract has a withdraw function which sends Ether to the calling address. Now the calling address could be a malicious contract such as this :
contract MaliciousContract {
VulnerableContract vulnerableContract = VulnerableContract(0x08970FEd061E7747CD9a38d680A601510CB659FB);
function deposit() public payable {
vulnerableContract.deposit.value(msg.value)();
}
function withdraw() public {
vulnerableContract.withdraw(1 ether);
}
function getBalance() view public returns(uint) {
return address(this).balance;
}
fallback () payable external {
if(address(vulnerableContract).balance > 1 ether) {
vulnerableContract.withdraw(1 ether);
}
}
}
When the malicious contract call the withdraw function, before reducing the balance of the malicious contract, it's fallback function will be called at it can steal more Ether from the vulnerable contract.
So by limiting the gas amount used by the fallback function up to 2300 gas we could prevent this attack. It means we can not put complex and expensive commands in the fallback function anymore.
Look at this for more information: https://swcregistry.io/docs/SWC-107
From Consensys article, they are saying use .call() instead of .transfer() and .send(). Only argument is that all three now sends more gas than 2300. Thus making it possible for reentrancy.
This comes to another conclusion that, regardless of all above, it is important to use checks-effects-interactions pattern to prevent reentracy attack.
Following smart contract works fine in Remix and Ganache. However doesn't work on private ethereum blockchains like Kaleido or Azure. What am I missing. When I call setA it consumes all gas and then fails.
pragma solidity ^0.4.24;
contract TestA {
uint public someValue;
function setValue(uint a) public returns (bool){
someValue = a;
return true;
}
}
contract TestB {
address public recentA;
function createA() public returns (address) {
recentA = new TestA();
return recentA;
}
function setA() public returns (bool) {
TestA(recentA).setValue(6);
return true;
}
}
I tried your contract in Kaleido, and found even calling eth_estimateGas with very large numbers was resulting in "out of gas".
I changed the setValue cross-contract call to set a gas value, and I was then able to call setA, and estimating the gas for setA showed just 31663.
recentA.setValue.gas(10000)(6);
I suspect this EVM behavior is related to permissioned chains with a gasprice of zero. However, that is speculation as I haven't investigated the internals.
I've also added eth_estimateGas, and support for multiple contracts in a Solidity file, to kaleido-go here in case it's helpful:
https://github.com/kaleido-io/kaleido-go
Another possibility for others encountering "out of gas" calling across contracts - In Geth if a require call fails in a called contract, the error is reported as "out of gas" (rather than "execution reverted", or a detailed reason for the require failing).
You are hitting the limit of gas allowed to be spent per block. Information about gas limit is included into every block, so you can check what's this value is right now in your blockchain. Currently on Ethereum MainNet, GasLimit (per block) is about 8 millions (see here https://etherscan.io/blocks)
To fix this, you can start your blockchain with modified genesis file. Try to increase value of gasLimit parameter in your genesis file, which specifies the maximum amount of gas processed per block. Try "gasLimit": "8000000".
Try to discard the return statement of setValue method in contract TestA.
pragma solidity ^0.4.24;
contract TestA {
uint public someValue;
function setValue(uint a) public {
someValue = a;
}
}
I have 2 basic contracts: one is for token and the second is for sale.
Token сontract:
contract MyToken is StandardToken, Ownable {
string public constant name = "My Sample Token";
string public constant symbol = "MST";
uint32 public constant decimals = 18;
function MyToken(uint _totalSupply) {
require (_totalSupply > 0);
totalSupply = _totalSupply;
balances[msg.sender] = totalSupply;
}
}
Sale Contract
contract Sale {
address owner;
address public founderAddress;
uint256 public constant foundersAmount = 50;
MyToken public token = new MyToken(1000);
uint256 public issuedTokensAmount = 0;
function Sale() {
owner = msg.sender;
founderAddress = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
token.transfer(founderAddress, foundersAmount);
}
function() external payable {
token.transfer(msg.sender, 1);
owner.transfer(msg.value);
}
}
StandardToken and Ownable are all standard implementations from OpenZeppelin repository. Full contract source is available here.
So basically in my Sale Contract I create an instance of my token contract with fixed supply and assign all of the tokens to the caller. Then I transfer some amount of tokens to founder address. When I try to send some ethereum to Sale contract I'm attempting to transfer some of my tokens to the sender (Running all code in Remix browser, I create an instance of Sale contract and call "fallback" method specifying some ether amount). However, this fails with "Exception during execution. (invalid opcode). Please debug the transaction for more information." message. All that I can see when debugging is that code fails in payable method at line:
token.transfer(msg.sender, 1);
I can't see the exact reason for this as I'm not able to step into this method and see whats going on inside.
Interesting thing is that when I remove a call to transfer method on token instance in Sale Contract constructor - code seems to run fine without any exceptions.
What am I missing?
I debugged into the the contract using remix, and the invalid opcode is thrown by:
290 DUP8
291 DUP1
292 EXTCODESIZE
293 ISZERO
294 ISZERO
295 PUSH2 012f
298 JUMPI
299 PUSH1 00
301 DUP1
302 INVALID
I left out the rest, but essentially it loads the address of the token contract and calls EXTCODESIZE which retrieves the contracts code size, and checks that it's not equal to 0 (the token contract exists), unfortunately, it did equate to 0. At this point, I'm unsure whether this is a limitation in remix or I've misunderstood the setup.
I tried the identical contract setup on truffle + testrpc and it deployed, accepted the currency successfully. Do note however that testrpc indicated:
Gas usage: 59137
Meaning that this is above the default sendTransaction w/ no data default (21,000 gas). This means that in a live environment, ensure that you inform users to include extra gas, otherwise the fallback function would probably fail due to OOG errors.
The reason behind this is that you're using a fallback function. Try using a normal function and it should happen.