0

I have this contract with dozen of variables

contract Test {
  uint256 public a = 1337;
  uint256 public b = 9001;
  string public c = "abc";
  string public d = "xyz";
  bool public e = true;
  bool public f = false;
  // and more
}

I want to be able to set each of them without having to manually specify setters. Is that possible?

Something like this

  function setVariable(string memory name, <?> value) public {
    this[name] = value; // error: can't index contract
  }

I would be fine with few setVariable function overloads for the different data types, which would still be better than polluting the contract with dozens of adhoc setters.

2 Answers 2

1

Not sure why this is useful, so I'll leave that up to the OP. Yes you can, in theory, using assembly.

In more detail, you can use assembly to read or write to any slot. The variables laid out map to slots. So, if the function has some knowledge of the mapping of strings to slots (hash(bytes(string)) => uint) which itself is probably stored in slot 0, then you might be close to the effect you want.

Have a look at this: https://solidity-by-example.org/app/write-to-any-slot/

Here is the code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

contract Storage {
    struct MyStruct {
        uint value;
    }

    // struct stored at slot 0
    MyStruct public s0 = MyStruct(123);
    // struct stored at slot 1
    MyStruct public s1 = MyStruct(456);
    // struct stored at slot 2
    MyStruct public s2 = MyStruct(789);

    function _get(uint i) internal pure returns (MyStruct storage s) {
        // get struct stored at slot i
        assembly {
            s.slot := i
        }
    }

    /*
    get(0) returns 123
    get(1) returns 456
    get(2) returns 789
    */
    function get(uint i) external view returns (uint) {
        // get value inside MyStruct stored at slot i
        return _get(i).value;
    }

    /*
    We can save data to any slot including slot 999 which is normally unaccessble.

    set(999) = 888 
    */
    function set(uint i, uint x) external {
        // set value of MyStruct to x and store it at slot i
        _get(i).value = x;
    }
}

Hope it helps.

0

It is possible to call (public) functions of a contract based on the function name, e.g. like this

function call(string calldata functionSignature, bytes calldata parameters) 
    external returns(bytes memory) {
    
    (bool success, bytes memory data) = address(this).delegatecall(
        abi.encodeWithSelector(
            bytes4(
                keccak256(bytes(functionSignature))
            ),
            parameters
        )
    );
    require(success, 'Call failed');
    return data;
}

and it is possible to get the value of a (public) variable (because the getter is automatically created for public variables), e.g. like this (for uint256)

function get(string calldata variableName) public view returns (uint256) {
    (bool success, bytes memory data) = address(this).staticcall(
        abi.encodeWithSelector(
            bytes4(
                keccak256(abi.encodePacked(variableName,"()"))
            )
        )
    );
    
    if (success) {
        return abi.decode(data, (uint256));
    }
}

but I don't think there's an equivalent to set a value. You'll need assembly for that I guess

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.