1

I am trying to call a function in another contract, that is protected with an openzepellin Role library.

I am calling availableFunction on the javascript side, which in turn calls protectedFunction. Even though I pass the adminRole address to the constructor of contract B, and then call availableFunction with adminRole, the function reverts.

In A.sol

contract interfaceB {
  function protectedFunc() public;
}

contract A {
  B b;

  constructor(address _B) {
    b = new interfaceB(_B);
  }

  function availableFunction() {
    b.protectedFunc();
  }
}

In B.sol

contract B {
  using Roles for Roles.Role;
  Roles.Role private adminRole;

  constructor(address _adminRole) public {
    adminRole.add(_adminRole);
  }

  function protectedFunc() public {
    require(adminRole.has(msg.sender), "DOES_NOT_HAVE_ADMIN_ROLE");
    ...
  }

}

In A.test.js

const A = artifacts.require("A");
const B = artifacts.require("B");

contract("A", function ([_, adminRole]) {
  beforeEach(async function () {
    b = B.new(adminRole);
    a = A.new(b.address);

  }
  describe("availableFunc", function () {
    it("should pass!", async function () {
      a.availableFunc({from: adminRole)}); // REVERTS!
    })
  }
})

Error: Returned error: VM Exception while processing transaction: revert DOES_NOT_HAVE_ADMIN_ROLE

1 Answer 1

1

I'm pretty sure your test calling b.protectedFunc(..) passes and if you call a.availableFunction() it will fail. When protectedFunc is called from A then msg.sender will be the address of A.

To make it work you have to use A address in the admin role.

const A = artifacts.require("A");
const B = artifacts.require("B");

contract("A", function ([_, adminRole]) {
  beforeEach(async function () {
    a = await A.new();
    b = await B.new(a.address);
  }
  describe("protectedFunc", function () {
    it("should pass!", async function () {
      a.availableFunction({from: adminRole)});
    })
  }
})
2
  • Thank you, that looks about right. I've changed my answer please, as there is an extra layer of complication that I missed - namely the constructor of contract A needs B.address! Is there any solution in this case please? Commented Feb 1, 2019 at 7:59
  • 1
    You have a circular dependency A needs B address and B needs A address. You have to split creation in two steps for example 1) contract creation, 2) initialize admin rol.
    – Ismael
    Commented Feb 1, 2019 at 17:24

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.