I'm going through the source code for the ERC-1155 token standard and I came across this block of code
bytes4 constant private INTERFACE_SIGNATURE_ERC165 = 0x01ffc9a7;
bytes4 constant private INTERFACE_SIGNATURE_ERC1155 = 0xd9b67a26;
function supportsInterface(bytes4 _interfaceID) override external view returns (bool) {
if (_interfaceID == INTERFACE_SIGNATURE_ERC165 ||
_interfaceID == INTERFACE_SIGNATURE_ERC1155) {
return true;
}
return false;
}
I don't quite understand what the benefit of this function is used for.
Also, where do you get the INTERFACE_SIGNATURE constants from?
Could someone please explain?
I believe there is a good explanation for this on openzeppelin docs.
but just to give a short answer, suppose you sent ERC20 tokens to a contract that lacks the ability to transfer it so these tokens are forever locked in the contract. To avoid this kind of things, when you do a safetranser it will first check whether the receiver is capable of receiving it or not and in that case this function is called.
As for INTERFACE_SIGNATURE you can give it a read here.
Related
I have one simple question.
So when we deal with smart contract on chain,
why don't we make encode function just in case for safety?
for example,
function encodeFunction(address _callee, bytes calldata _callData, uint256 _value) public returns (bool) {
(bool success, bytes memory returnData) = callee.call{vaule: _value}(_callData);
require(success, "tx failed");
return success;
}
Suppose that we are able to figure out any contract address and calldata, Isn't it much safer to have this kind of function to deal with any situation?
Wouldn't someone just listen to the encodeFunction and its parameters? For example etherscan allows users to see function calls and parameters .That would defeat the whole purpose of your encode function.
Also having this kind of function defeats the whole purpose of transparency of the concept blockchain.
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 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.
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.
I am looking for an efficient way to check if the the person invoking a function is allowed to access a function defined in a smart contract by comparing msg.sender with array of addresses that I am hardcoding at the time of deployment in solidity. I can do it using for loop but that takes a lot of gas. Thanks in advance.
Use mapping to store this.
mapping(address => bool) public blacklisted;
...
require(!_blacklisted[msg.sender]);
You could also use it as a modifier:
modifier notBlacklisted() {
require(!blacklisted[msg.sender]);
_;
}
then in a function:
function foo() notBlacklisted public
{
// do stuff
}
only users who are not blacklisted will be able to use the function.
At first I was tempted to tell you there's no way around it, but then I got intriguing idea and it turned out to work out pretty well! Note however, that this will only work in case you know the addresses at compile time.
Note: In my experiment, I've modified the task a bit - I will be comparing msg.sender to list of blacklisted addresses. Changing this to your usecase is straightforward.
Intuitive solution
The intuitive way to implement the functionality you are proposing would be probably following:
pragma solidity ^0.4.18;
contract DataInStorage {
uint counter;
address[] public blackListed = [ 0x281055afc982d96fab65b3a49cac8b878184cb16, 0x6f46cf5569aefa1acc1009290c8e043747172d89 ]
function increment() public {
for (uint i=0; i<blackListed.length; i++) {
require(msg.sender != blackListed[i]);
}
counter += 1;
}
}
The gas costs turned out to be following:
<number of addresses> | <gas spent>
100 | 124808
200 | 207708
So it seems that the cost of one iteration is about 829 (829 * 100 = 82900 = 207708 - 124808)
Why is this expensive?
First of all, why is iterating addressed and comparing them to msg.sender expensive? It's not because of the for loop, it's not because of the if comparisons. The reason is that EVM needs to read from contract storage. Reading and writing to storage are the most expensive operations.
Contract code vs Contract storage
So here comes the idea. What if we don't put these addresses into contract storage, but contract code instead? Let's give it a try
pragma solidity ^0.4.18;
contract DataInCode {
uint counter;
function increment() public {
require(0x281055afc982d96fab65b3a49cac8b878184cb16 != msg.sender);
require(0x6f46cf5569aefa1acc1009290c8e043747172d89 != msg.sender);
counter += 1;
}
}
I've tried this pattern with higher number of addresses, and here's an interesting result
<number of addresses> | <gas spent>
100 | 45338
200 | 49038
So one address verification costs us in this case only 37 gas. That's about 22 times better than the solution with contract storage.
If someone can provide more technical explanation why loading code in EVM is much cheaper than loading data storage, I would love to hear it.
Full example
Data In Code (100 addresses) https://ethfiddle.com/mDB9OM2azy
Data In Code (200 addresses) https://ethfiddle.com/nIt2QwBLTX
Data In Storage (100 addresses) https://ethfiddle.com/Wg5CMAN0OU
Data In Storage (200 addresses) https://ethfiddle.com/5NVVCVUT0R
Tested with Truffle