I have a contract which inherits from contract Claimable
by OpenZeppelin (see here):
pragma solidity ^0.4.24;
import "./Claimable.sol";
contract MyTestContract is Claimable {
uint256 public id;
function set(uint256 _id) external onlyOwner {id = _id;}
}
The purpose of course is to restrict the calling permission for function set
.
This function can be called only from the account which I use in order to deploy the contract.
After deployment, I would like to transfer ownership from this account to another contract.
The reason for this is not a part of my question, but for all it matters, I need to enforce multiple-signature on the function, and allow it to be executed only after X out of Y permitted users have submitted a request. Hence I need to transfer the ownership of the contract from my account to a MultiSig contract by Gnosis (see here), which I deploy myself.
The issue which I am having a problem with and cannot really wrap my head around, is how to transfer ownership to something which is "not exactly an account".
When I transfer ownership from my account to another account on the network, it works fine.
For example, I set up a Ganache network with 8 accounts, and then run this Truffle test:
contract('MyTestContract', (accounts) => {
it('Transfer Ownership', async () => {
let myTestContract = await artifacts.require('MyTestContract').new();
let owner = await myTestContract.owner();
assert(owner === accounts[0]);
await myTestContract.transferOwnership(accounts[1]);
await myTestContract.claimOwnership({from: accounts[1]});
let newOwner = await myTestContract.owner();
assert(newOwner === accounts[1]);
});
});
But when I transfer ownership from my account to the address of a contract which I deploy into the network, Truffle reports:
Error: sender account not recognized
Now, the error by itself makes sense, because there is no such account on the network.
So my question here – is it even possible to do what I'm trying to achieve?
I feel that I have some fundamental misunderstanding of the difference between accounts and contracts, which I'd be very grateful if someone could clarify for me.
Thank you very much!
UPDATE:
To clarify, this is how I transfer ownership to the MultiSig contract:
await myTestContract.transferOwnership(multiSigContract.address);
await myTestContract.claimOwnership({from: multiSigContract.address});
And by the way, when I use Ownable
instead of Claimable
, i.e.:
On-Chain Code:
contract MyTestContract is Ownable {...}
Off-Chain Code:
await myTestContract.transferOwnership(multiSigContract.address);
The ownership-transfer completes successfully, and the MultiSig contract is the only one which can call the set
function.
So the problem is essentially at:
await myTestContract.claimOwnership({from: multiSigContract.address});
Best Answer
await myTestContract.claimOwnership({from: multiSigContract.address});
is where the problem lies. You're telling truffle to send a transaction signed by an address it does not control. Thus the error.
You can think about the Gnosis MultiSigContract as a "Relay" that you post transactions to, which then require approvals to go through ( if required confirmations is higher than 1 ).
In order for the "contract" to "claimOwnership" of your main contract you need to create a transaction that does just that:
Flow:
The hard part is step 4, which takes you in the inner workings of how solidity abi calls are built. You can choose to manually do it ( bytes4(sha3("contract_method(bytes,bool,uint256[])") ), or let the truffle instance.contract.metod.getData helper do it for you.
Take a look at https://github.com/gnosis/MultiSigWallet/blob/master/test/javascript/testExternalCalls.js
Your final code should look similar to: ( untested )