The easiest solution for this would be to create a getter function in the other contracts, that share the same name. Then you can just call that function from contract A to get the variable.
interface Interface {
function getVariable() external view returns (uint256);
}
contract A {
function callExternal(address externalContract) public view returns (uint256) {
return Interface(externalContract).getVariable();
}
}
contract B is Interface {
uint256 public foo;
function getVariable() override view external returns (uint256) {
return foo;
}
}
contract C is Interface {
uint256 public bar;
function getVariable() override view external returns (uint256) {
return bar;
}
}
Now you can simply call A.callExternal(<address of B/C>);
to get the value.
Alternatively, you can use staticcall
to get the variables from a string name, since public variables are essentially getters on their own. This requires a bit more extra code though, to get the function selector from a string:
contract A {
function callExternal(address externalContract, string calldata variableName) public view returns (uint256) {
(bool success, bytes memory data) = externalContract.staticcall(
abi.encodeWithSelector(
bytes4(
keccak256(
abi.encodePacked(
variableName,
"()"
)
)
)
)
);
if (success) {
return abi.decode(data, (uint256));
}
}
}
Now calling A.callExternal(<address of B>, "foo")
will return foo
from contract B. Note that the variables must be public for this to work.