1

I have tried to implement a persistent data storage with smart contracts as seen with the following code (this is an example that mimics what my actual code does). I have a data storage contract with a mapping of contract addresses to another mapping of more data so that each calling contract can have access to their own set of data, taken from this article https://medium.com/quillhash/how-to-write-upgradable-smart-contracts-in-solidity-d8f1b95a0e9a. Setters can only be set for msg.sender, i.e., the calling contract. Getters can be accessed by anyone, e.g. from "OtherContract" below since he has access to the "UserContract" address. (Side note: I inherit from SharedEvents since I do something similar in my actual code and I include this here just in case this might affect the weird behavior I am seeing.)

pragma solidity ^0.5.0;

contract ExampleDataStorage {

struct UserProfile {
    string username;
    string department;
    uint8 avatarNumber;
    uint40 totalSteps;
    mapping(uint32 => uint32) userStepInfo;
    uint32 lastSyncDate;
}

mapping(address => mapping(address => UserProfile)) _userProfile;

function setAvatarNumber(uint8 _avatarNumber, address _userAddress) public {
    _userProfile[msg.sender][_userAddress].avatarNumber = _avatarNumber;
}

function getAvatarNumber(address _contractAddress, address _userAddress) public view returns(uint8) {
    return _userProfile[_contractAddress][_userAddress].avatarNumber;
}

}

contract SharedEvents {

constructor () public {}

event avatarSet(string _contractName, uint8 _avatar);

}


contract UserContract is SharedEvents {

ExampleDataStorage _storage;
string _contractName = "UserContract";

constructor (address _exampleDataStorageAddress) public payable {
    _storage = ExampleDataStorage(_exampleDataStorageAddress);
}

function _setAvatarNumber(uint8 _newAvatarNumber) public {
    _storage.setAvatarNumber(_newAvatarNumber, msg.sender);
    emit avatarSet(_contractName, _newAvatarNumber);
}

function _getAvatarNumber() public view returns(uint8) {
    return _storage.getAvatarNumber(address(this), msg.sender);
}

}


contract OtherContract is SharedEvents {

ExampleDataStorage _storage;
address _userContractAddress;

constructor (address _exampleDataStorageAddress, address _userContractAddressParam) public payable {
    _storage = ExampleDataStorage(_exampleDataStorageAddress);
    _userContractAddress = _userContractAddressParam;
}

function _getAvatarNumber() public view returns(uint8) {
    return _storage.getAvatarNumber(_userContractAddress, msg.sender);
}

}

Below is the truffle migration code.

module.exports = function(deployer) {
    deployer.deploy(ExampleDataStorage)
    .then(() => {
        return deployer.deploy(UserContract, ExampleDataStorage.address)
    })
    .then(() => {
        return deployer.deploy(OtherContract, ExampleDataStorage.address, UserContract.address)
    })
}

When this is run in remix, everything works fine. When I deploy with truffle all contracts are compiled. When I use web3 is when I see problems occur. I am using web3@^1.0.0-beta.33 and truffle@^4.1.14. When I use web3 and call the setavatar function from UserContract, I get no errors. When I use web3 and call the getter from the ExampleDataStorage contract (in a function like below), I get the default value 0 for uint8 instead of the value I set before.

getAvatarNumber (address) {
    return this.contractHandler.methods.getAvatarNumber(this.UserContractAddress, address).call()
}

I am sure that I am passing in the correct contract addresses in truffle so I have no idea why I am not correctly accessing the data I should be getting. Inside my actual code, I also added a test function inside UserContract that prints out both the ExampleDataStorage and UserContract addresses to make sure I am using the correct addresses when setting and getting. Any help would be greatly appreciated. Thank you very much.

EDIT

I figured out what the error was. I implemented the above code in my project and it worked fine. Then I took my original code and tried to implement the same thing but got the same errors. The problem was in some functions I was trying to return a value from the data storage contract to the child user contract before setting a value. Here is an example. If ExampleDataStorage had a function that returns a said value, e.g.,

function returnRandomValue() public view returns(uint) {
    return somethingFromStorage[0];

}

and I call this function from the child contract like this:

function _setAvatarNumber(uint8 _newAvatarNumber) public {
    uint _something = _storage.returnRandomValue();
    _storage.setAvatarNumber(_newAvatarNumber, msg.sender);
    emit avatarSet(_contractName, _newAvatarNumber);
}

then for some reason the error occurs. So for me to fix it, I can just make sure to not call any getters from within the child contract from the parent contract. However, in some cases this might not be ideal as in the future I would need contract interaction, specifically getting the value from another contract's storage (called from another contract) and then using that return value for something else. Does anyone know why this happens?

0

Sorry, but I can't reproduce your error. (unfortunately, because I have a problem which I thought could be related to this)

Anyways, I deployed your contracts to the Rinkeby Test Net and everything is working fine for me.

Contracts on Rinkeby Testnet at:

  • ExampleDataStorage: 0xb27e0cda46acaf80f353ceef04c69989a0c813e3
  • UserContract: 0x5d92c369fb365b5e2d2434191c42e1d3bb2ae902
  • OtherContract: 0xb8e4daaf5003ce6663524ef1ba1fee7a35fbde70

For the following HTML you need to set your avatar number in the contract before you can test the functionality (because you obiously don't have my private key so you msg.sender won't be the same for you).

<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
    <script src="https://github.com/ethereum/web3.js/blob/develop/dist/web3.min.js" type="text"></script>
    <h1>My Test</h1>
    <div id="output"></div>
    <button id="button">Click</button>
    <script>
        window.addEventListener('load', function() {
            if(typeof web3 !== undefined) web3js = new Web3(web3.currentProvider);

            document.getElementById("button").addEventListener("click", function() {
                getAvatarNumber();
            }, function(error, result){}, false);
        });

        var contractInstance;
        var contractABI = [
    {
        "inputs": [
            {
                "name": "_exampleDataStorageAddress",
                "type": "address"
            },
            {
                "name": "_userContractAddressParam",
                "type": "address"
            }
        ],
        "payable": true,
        "stateMutability": "payable",
        "type": "constructor"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": false,
                "name": "_contractName",
                "type": "string"
            },
            {
                "indexed": false,
                "name": "_avatar",
                "type": "uint8"
            }
        ],
        "name": "avatarSet",
        "type": "event"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "_getAvatarNumber",
        "outputs": [
            {
                "name": "",
                "type": "uint8"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
];
        var contractAddress = "0xb8e4daaf5003ce6663524ef1ba1fee7a35fbde70";
        contractInstance = web3.eth.contract(contractABI).at(contractAddress);


        function getAvatarNumber() {
            contractInstance._getAvatarNumber(function(error, result){
                document.getElementById("output").innerHTML = result;
                console.log(result);
            });
        }
    </script>
</body>
</html>
  • Thank you Felix for your time and effort. Since I see that it works for you, I will attempt to solve my problem from a blank slate to possibly understand what is going wrong. When I find a solution (hopefully soon) I will post the problem and solution here. – Michael Pont Nov 30 '18 at 18:32

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.