Way in solidity to match users address with address defined in array - ethereum

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

Related

What is Interface signature in solidity?

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.

Best plan of attack for digital certificates for proof of completion on EVM

I am looking to explore the option of creating a digital certificate (as in proof) when someone has completed a portion of training, and for this to be issued on an EVM-compatible blockchain using Solidity.
I have prototyped using ERC721 NFTs to encode a "certificate" however, I'd like to prevent recipients from being able to transfer these certificates. To prevent transfer, I attempted to use the Pause.sol functionality from OpenZeppelin, however, this would result in the entire contract being paused, as opposed to a specific tokenId.
Does anyone have any recommendation on an approach? Am I overcomplicating it if I don't want recipients to be able to trade the certificates (i.e. for them to remain static)? Any pointers would be much appreciated!
The simplest and most raw solution is to just set a mapping value.
pragma solidity ^0.8;
contract TrainingResults {
enum Stage {
NONE,
STAGE_1,
STAGE_2,
COMPLETED
}
mapping (address => Stage) public participantStage;
function setParticipantStage(address _graduate, Stage _stage) external {
require(msg.sender == address(0x123), "Not authorized");
participantStage[_graduate] = _stage;
}
}
Or if you want them to be able to see some kind of NFT in their wallet (that supports NFTs), you can modify the ERC-721 contract to disallow transfers.
For example the OpenZeppelin implementation uses a function named _beforeTokenTransfer() (GitHub link) that can be overwritten to disallow transfers altogether.
pragma solidity ^0.8;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract TrainingResults is ERC721 {
constructor() ERC721("TrainingResults", "TR") {}
function _beforeTokenTransfer(address from,address to, uint256 tokenId) override internal {
// Allow only for the admin
// as this function is called on token mint as well
require(msg.sender == address(0x123), "Cannot transfer tokens");
}
}

Ethereum Transaction Error while calling a contract function from another contract

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;
}
}

Retrieving multidimensional balance

I am developing an Ethereum based Card Game. A user can collect n amount of individual/unique Cards. Everything is up and running, I am using the following balance mapping:
mapping (address => mapping (uint256 => uint256)) balances;
The first uint is the Card ID, the second uint is the Card count. I will have up to 1000 Cards, right now I am testing with 700 Cards.
I retrieve the balances on DApp Start by calling:
function balanceOf(address _owner, uint256 _id) view external returns(uint256) {
return balances[_owner][_id];
}
for every single ID. On balance changes I do partial balance updates. This generally works. It is free, but it is also extremely slow as the initial retrieval call has to be done 640 times. I have researched a lot and also tried various implementations, but the main problem is that I need to retrieve an address mapped array holding the Card ID and Count information. Currently you can not easily retrieve dynamic sized Arrays or Structs.
What would be the proposal to resolve the issue? Am I stuck with up to 1000 balanceOf calls on DApp Start until Solidity introduces simple Array calls?
I thought about caching data on my WebServer, but for this to work I would need to run a node on the WebServer which I would like to avoid.
A Client based caching, where the Client posts the balance to the WebServer may also run into an inconsistent state because of the asynchronous nature of the Blockchain.
You can also use struct for managing data more easily. I made one contract using struct and retained data too. Please let me know if this approach didnt work for you.
pragma solidity ^0.4.18;
contract Test{
struct User{
uint cardId;
uint cardCount;
}
address user_address;
mapping (address => User) public Users;
function add_user() public {
user_address = msg.sender ;
var new_user = Users[user_address];
new_user.cardId =2;
new_user.cardCount = 50;
}
function get() public returns(uint,uint)
{
return(Users[user_address].cardId,Users[user_address].cardCount);
}
}
Your best chance is to use a secondary mapping to store the card IDs some user has, query it first, and than, for each ID, check the count for that user and that card ID.
Here is the code, tested on Remix:
pragma solidity 0.4.24;
contract Test {
mapping (address => uint[]) cardsOwned;
mapping (address => mapping (uint => uint)) cardsCounter;
function cardsOwnedBy(address _owner) view public returns (uint[]) {
return (cardsOwned[_owner]);
}
function cardsCounterFor(address _owner, uint _id) view public returns (uint) {
return cardsCounter[_owner][_id];
}
}
I kept on trying various different solutions but couldn't find any good way to handle this. I found a setup which will work for me for now, until Solidity is updated to be more functional when it comes to array handling and especially dynamically sized variables.
Fastest solution for my requirements:
mapping (address => uint256[1000]) public balances;
The mapping now assigns addresses to a fixed size array. I can now retrieve the full list on DApp Start by using:
function balancesOf(address _owner) view external returns (uint256[1000]) {
return balances[_owner];
}
The main advantage is that it is extremely fast, compared to any other solution. The main disadvantage is, that I lost my dynamically sized array and I have to know the maximum Card Count in advance - which I do not. I used a safety buffer now but if I hit the 1000 Cards mark I will have to update the contract. Hopefully there will be better solutions in the future.

How to detect if an Ethereum address is an ERC20 token contract?

If I only get an Ethereum address from the input, is there a way to find out whether it matches the ERC20 token standard?
ERC165 tackles this problem, but, unfortunately, most ERC20 implementations don't support it (as of Nov 2018, at least OpenZeppelin doesn't). This means that you could try calling the supportsInterface function, but it would revert anyway and you'd rather complicate things.
Nevertheless, here's how it's defined in ERC721:
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
Although it's not guaranteed for all implementations to define the interface id, there's a higher chance it will work in the case of ERC721, given the fact the community agreed on applying ERC165 right from the get-go. If the return value of the query below is true, then it means you have a compliant contract, otherwise just revert the transaction.
// you can call this in your contracts
IERC721(contractAddress).supportsInterface(0x80ac58cd)
Also, a useful resource for manually checking the bytes4 of a given method is 4byte.directory
If you are asking about off-chain, so use these functions:
getContract(url, smartContractAddress){
const Web3Eth = require('web3-eth');
const abi_ = this.getABI();
const web3Eth = new Web3Eth(Web3Eth.givenProvider || url);
return new web3Eth.Contract(abi_, smartContractAddress);
}
async getERCtype(contract){
const is721 = await contract.methods.supportsInterface('0x80ac58cd').call();
if(is721){
return "ERC721";
}
const is1155 = await contract.methods.supportsInterface('0xd9b67a26').call();
if(is1155){
return "ERC1155";
}
return undefined;
}
getABI(){
return [
{"constant":true,"inputs": [
{"internalType":"bytes4","name": "","type": "bytes4"}],
"name": "supportsInterface",
"outputs": [{"internalType":"bool","name": "","type": "bool"}],
"payable": false,"stateMutability":"view","type": "function"}
];
}
like this:
const contract = getContract(url, smartContractAddress);
const type = await getERCtype(contract);
console.log(type);
There are many possible ways to achieve this. One possible quick and dirty solution is to check if a ERC20 function exists on the contract address by calling the following:
eth.call({to:contractAddress, data:web3.sha3("balanceOf(address)")})
A non-ERC20 will return a 'null' 0x hex response whereas an ERC20 will give you a 32byte uint, in this case 0 but if you provide an address in the data then it will give you the actual token balance for that address.
This is not a guaranteed way of determining a contract is ERC20 since other contracts may expose the same function, however it is a quick and easy check. You could add additional calls on totalSupply() etc. for more confirmation.
the question states that given an Ethereum address, is this an Ethereum contract address or not?
First thing, you have to decide if this address is a contract address or an externally owned account address. How to find out if an Ethereum address is a contract
If it is a contract address, then you can only get the bytecode that is stored in the blockchain. Now you have to reverse engineer the bytecode to get the contract functions but this is almost impossible. There are some decompilers but they are not perfectly creating the contract code from the bytecode.
ERC165 just checks if the current contract signature supports the given interfaceId or not. Since you have only ethereum address, this will not help in this question.