I was testing delegate calls with the proxy pattern for the upgradable contract. Things were going good till the time I added onlyOwner
modifier.
My Contracts
pragma solidity 0.5.8;
contract TestLogicInterface {
function getX() public view returns (uint);
function setX(uint _newX) public;
function owner() public view returns(address);
function setOwner(address _owner) public;
function testMessageSender() public view returns(address, address);
}
contract TestState {
uint x = 12;
function getX() public view returns (uint) {
return x;
}
function setX(uint _newX) public {
x = _newX;
}
}
contract TestLogic {
TestState public _dataLayer;
address public owner;
constructor(address _dl, address _owner) public {
_dataLayer = TestState(_dl);
owner = _owner;
}
modifier onlyOwner {
require(msg.sender == owner, "Only owner is allowed");
_;
}
function setOwner(address _owner) public {
owner = _owner;
}
function setX(uint _newX) public onlyOwner {
_dataLayer.setX(_newX);
}
function getX() public view returns (uint) {
return _dataLayer.getX();
}
function testMessageSender() public view returns(address, address){
return (msg.sender, owner);
}
}
contract TestProxy {
address public targetAddress;
constructor(address _lc) public {
setTargetAddress(_lc);
}
function setTargetAddress(address _address) public {
require(_address != address(0));
targetAddress = _address;
}
function () external {
address contractAddr = targetAddress;
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)
let result := delegatecall(gas, contractAddr, ptr, calldatasize, 0, 0)
let size := returndatasize
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}
Test Cases
const TestState = artifacts.require("TestState")
const TestLogic = artifacts.require("TestLogic")
const TestProxy = artifacts.require("TestProxy")
const LogicInterface = artifacts.require("TestLogicInterface")
let proxy, states, logic, proxyInterfaced
contract('test upgrade', async(accounts) => {
beforeEach(async() =>{
state = await TestState.deployed()
logic = await TestLogic.deployed()
proxy = await TestProxy.deployed()
proxyInterfaced = await LogicInterface.at(proxy.address);
await proxyInterfaced.setOwner(accounts[0])
})
it("logic_state", async() => {
let x = (await proxyInterfaced.getX()).toNumber()
console.log({x})
let msgSender = await proxyInterfaced.testMessageSender()
console.log({msgSender})
await proxyInterfaced.setX(11, {gas: 300000})
})
})
I added another function testMessageSender
to check if msg.sender is correct and matches the owner. The result of the function was:
msg.sender: '0x4c256B6945a3FFCbf93463D8c0ff914C533bC0Aa',
owner: '0x4c256B6945a3FFCbf93463D8c0ff914C533bC0Aa'
Clearly they both are the same, then why is my test case not running. I am getting an error:
Error: Returned error: VM Exception while processing transaction: revert Only owner is allowed -- Reason given: Only owner is allowed.
If I remove the modifier, everything works perfectly. What am I missing?
Edit 1:
I updated my setX
function and added event to check if my assertion is correct.
event test(address _owner, address _sender);
function setX(uint _newX) public {
_dataLayer.setX(_newX);
emit test(owner, msg.sender);
}
and I can see when I call this function from tests, 2 events are fired instead of one with following data.
test(_owner: 0x4c256B6945a3FFCbf93463D8c0ff914C533bC0Aa (address), _sender: 0xC63268D4082Dd102cC2730cD606B0f9EedC8B9E2 (address))
test(_owner: 0x4c256B6945a3FFCbf93463D8c0ff914C533bC0Aa (address), _sender: 0x4c256B6945a3FFCbf93463D8c0ff914C533bC0Aa (address))
The second one says both msg.sender and owner are same while first one says they are different. I am more puzzled now.
Why 2 events are fired instead of one?
Edit 2:
If I don't use State Contract and keep states in Logic Contract itself, everything works perfect.
I noticed another absurd behavior. If I change methods of my TestState
from getX
to getterX
. The test cases fail, and If I rename them back to getX and setX, everything works again.
Is there a corelation between names of function of state and logic contract?