Below is a very simple smart contract which has a struct Instructor which has 3 attributes right now i.e. age, fName, and lName. Now if tomorrow I wish to add a new attribute I will need to update all the places where this struct was being used in the respective smart contract, which is a bad point from scalability and code maintenance perspective.

so question, Is there a better way of handling this situation?

pragma solidity ^0.4.18;

contract Courses {

struct Instructor {
    uint age;
    string fName;
    string lName;

mapping (address => Instructor) instructors;
address[] public instructorAccts;

function setInstructor(address _address, uint _age, string _fName, string _lName) public {
    instructors[_address].age = _age;
    instructors[_address].fName = _fName;
    instructors[_address].lName = _lName;

    instructorAccts.push(_address) -1;

function getInstructors() view public returns(address[]) {
    return instructorAccts;

function getInstructor(address _address) view public returns (uint, string, string) {
    return (instructors[_address].age, instructors[_address].fName, instructors[_address].lName);

function countInstructors() view public returns (uint) {
    return instructorAccts.length;

  • You could include an 'extraInfo' bytes[] field in your struct and pass it any additional data in json format - this might not be the tidiest way of doing it though so I'm interested in seeing any other ways people can come up with! – TC8 Mar 5 '18 at 18:56
  • I thought of it as well, something which we use a lot of time in a web application, to handle dynamic fields is storing JSON in the database as a column for metaData but the challenge I see here in Solidity is that might lead a lot of iteration or IF/ELSE check in the JSON for newly added columns or old removed columns – Shuja Ahmed Mar 5 '18 at 19:36

You can use nested mappings, it is kind ugly but you can hide some of the complexity in a library

contract Db {

    mapping (address => mapping (bytes32 => bytes32)) data;


    function getAge(address usr) public view returns (uint) {
        return getUint(usr, keccak256('age'));

    function setAge(address usr, uint age) public {
        setUint(usr, keccak256('age'), age);

    function getName(address usr) public view returns (string) {
        return getString(usr, keccak256('name'));

    function setName(address usr, string name) public {
        setString(usr, keccak256('name'), name);

    // Low level access/storage

    function getUint(address usr, bytes32 ky) internal view returns (uint) {
        return uint(data[usr][ky]);

    function setUint(address usr, bytes32 ky, uint val) internal {
        data[usr][ky] = bytes32(val);

    function getString(address usr, bytes32 ky) internal view returns (string) {
        return bytes32ToString(data[usr][ky]);

    function setString(address usr, bytes32 ky, string val) internal {
        data[usr][ky] = stringToBytes32(val);

    // Utility functions to pack/unpack a string into bytes32

    function bytes32ToString(bytes32) internal returns (string) {

    function stringToBytes32(string) internal returns (bytes32) {


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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