1

I am researching Solidity as a backend, and I have created this smart contract. I want to use it for user authentication and storing their data inside the smart contract. I want users to automatically be added to the list of authorized users when they register through Metamask if they have special tokens with address X, and they should be able to store their data as a string. The data should be sorted by type, and retrieving data should be done by the address of the authorized user and the type of data that needs to be retrieved. Please help me understand this task before I get completely confused.

// SPDX-License-Identifier: AGPL
pragma solidity ^0.8.0;

// Contract Definition
contract GyberStorage{

// Owner address
    address private owner;

    // Addresses that can call contract functions

    address[] private authorizedAddresses;

    // Data structure

    struct Data{
        uint256 id;
        string dataType;
        address userAddress;
        string link;
    }

    // Data storage
    mapping(uint256 => Data) private data;

    // Contract constructor
    constructor(){
        owner = msg.sender;
    }

    // Modifier to check that the function is called only by the contract owner
    modifier onlyOwner(){
        require(msg.sender == owner,'Only owner can call this function');
        _;
    }

    // Modifier to check that the function is called only by an authorized address
    modifier onlyAuthorized(){
        // Initializing a flag variable
    bool isAuthorized = false;

        // Checking if the calling address is authorized
    for (uint i = 0;i < authorizedAddresses.length;i++){
        if (authorizedAddresses[i] == msg.sender){
            isAuthorized = true;
            break;
        }
    }
    
       // If the address is not authorized, an exception is generated
    require(isAuthorized,"Not authorized");
    _;

    }

    // Function to add an authorized address
    function addAuthorizedAddress(address _address) public onlyOwner {
        authorizedAddresses.push(_address);
    }

    // Function to remove an authorized address
    function removeAuthorizedAddress(address _address) public onlyOwner {
        for (uint i = 0; i < authorizedAddresses.length; i++){
            if (authorizedAddresses[i] == _address){
                delete authorizedAddresses[i];
            }
        }
    }

    // Function to add data to the storage
    function addData(uint256 id, string memory dataType, address userAddress, string memory link) public onlyAuthorized {

        // Checking that the data with the given ID has not been added yet
    require(data[id].id == 0);

        // Adding data to the storage
    data[id] = Data(id, dataType, userAddress, link);

    }

    // Function to retrieve data from the storage
    function getData(uint256 id,address userAddress,string memory dataType ) public view returns (uint256, string memory, address , string memory){
        // Checking that the data with the given ID is in the storage
    require(data[id].id != 0,"Data with this ID does not exist");

        // Returning the data
    return (data[id].id, data[id].dataType, data[id].userAddress, data[id].link);
    }

    // Function to update data in the storage
    function updateData(uint256 id, string memory dataType, address userAddress, string memory linkIpfs) public onlyAuthorized{

        // Checking that the data with the given ID is in the storage
    require(data[id].id != 0,"Data with this ID does not exist");
    // Removing data from the storage
    delete data[id];
    }

}

I am interested in how to modify the code to match the functionality I described, and I would appreciate any general security and performance recommendations for the smart contract.

2 Answers 2

0

A quick tip I could give you enhance the security and performance on your code would be to use a mapping instead of an array for the authorizedAddresses.

  • The reason is because if the list of authorizedAddresses grows up substantially it might cause transactions to run out of gas while iterating over the array and your transactions will always revert, causing a Denial of Service in your contract.

  • If what you are looking to accomplish is to verify if a certain address/the caller is authorized to do something, you can use mapping(address=>bool) and set to true all those authorized addresses

From:

// Addresses that can call contract functions
address[] private authorizedAddresses;

To

// Addresses that can call contract functions
mapping(address => bool) private authorizedAddresses;

And you could create a separate function that you'll need to call to set to true the authorizedAddresses, you can get creative on this one, you could set to true one address at a time, receive an array of addresses and set them to true (individually) in the same call

.....

Hope this helps you and gives you some perspective about good practices.

With regards of the other aspects you are looking to implement in the contract, be mindful that the computational power that functions can handle is bounded by the gas, and the more resources a transactions consumes, the more real money is spent to process them

2

Don't use Solidity as a backend! Here is why:

  1. It costs a lot of money! Much more than paying for server costs or using a free auth provider.
  2. Every interaction has to be signed and be paid for by your dapp users (imagine having to log into SO everytime you change from whitemode to darkmode.)
  3. It is not easily updatable/extendable. Since Smart Contracts are immutable and can never be changed after deployment. E.g. you cannot extend your Data struct or fix security vulnerabilities after deployment.
  4. Sorting is just not feasible with the little execution-time you have. Either you have a lot of overhead when adding/deleting/modifing the on-chain data. In this approach, the amount a user would have to spend by doing these transactions scales with the number of total users of your contract, which seems unfair. Alternatively, you need to run a node to query all of the data, to do the sorting server side (which would defeat the purpose of using solidity as a backend).

I want users to automatically be added to the list of authorized users when they register through Metamask

This is not possible, since gas costs are usually paid for by the user interacting with the contract, you cannot do anything automatically. There is a way of paying for the user with a Gas Station Network but this is quite complex and I don't think that you really want to pay for your users as again interacting with the blockchain is still much more expensive than just using a centralized third-party auth provider. What you probably want is something like Firebase Auth that is free. Then you store the address of the user together with the account.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.