Solidity – How to Transfer Contract Ownership from One Contract to Another Using Hardhat

hardhatsolidity

I'm a newbie here so take it easy. My setup is that I'm trying avoid redundancy.
I have 2 contracts:
1 = Factory.sol
2 = Project.sol

The factory creates new projects which is initiated by the user.
As a result the owner of the projects is the Factory
and the creator of the project is the user.

If in the future I need to update my Factory.sol or Project.sol I'm going to be left with all the oringial Projects still attached to the Original Factory address.

So I want to work out how to transfer the Projects created to a new contract e.g. from Factory.sol(v1) to Factory.sl(v2).

I've made the Project.sol ownable

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";
import "./@openzeppelin/contracts/access/Ownable.sol";

contract Project is Ownable{

The reason this gets tricky is because I'm not the owner of the contract and the Ownable.sol has that restriction of onlyOwner.

Has anyone tried to create this kind of setup before?


Steps taken:
I created a file called ProjectInterface.sol and place the following code:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

interface Project {
    function transferOwnership(address newOwner) external;
}

When I tried to add

Import "./ProjectInterface.sol" 

I get an error so I left that our:

DeclarationError: Identifier already declared.
 --> contracts/Crowdfunding.sol:6:1:
  |
6 | import './ProjectInterface.sol';
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note: The previous declaration is here:
  --> contracts/Project.sol:14:1:
   |
14 | contract Project is Ownable{
   | ^ (Relevant source part starts here and spans across multiple lines).


Error HH600: Compilation failed

I added the following code to Factory:

function transferProjectOwnership(address _projectAddress , address newOwner) public  {
        Project(_projectAddress).transferOwnership(newOwner);
    }

Then in my deploy scrip this line is the one that calls the factory and is the line contrinuting to the below error:

const transfer = await FactoryContractA.connect(address1).transferProjectOwnership(event.events[1].args.projectContractAddress,FacotryContractB)

cheers I'll do some reading up on them tonight. So I've tried both approaches and when running the call from a hardhat test I just get jiberish error through on the call line:
Error: invalid address or ENS name (argument="name", value={"interface":{"fragments":[{"name":"ContributionReceived","anonymous":false,"inputs":[{"name":"projectAddress","type":"address","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"contributedAmount","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true},{"name":"comments","type":"string","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"string","_isParamType":true},{"name":"contributor","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"img","type":"string","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"string","_isParamType":true}],"type":"event","_isFragment":true},{"name":"ProjectStarted","anonymous":false,"inputs":[{"name":"projectContractAddress","type":"address","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"creator","type":"address","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"projectDeadline","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true},{"name":"goalAmount","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true},{"name":"currentAmount","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true},{"name":"noOfContributor","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true},{"name":"cate","type":"string","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"string","_isParamType":true},{"name":"title","type":"string","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"string","_isParamType":true},{"name":"desc","type":"string","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"s

I can't understand the error and I'm feeling a bit stuck at this stage. I will read up on interfaces, but if anyone has help in the meantime that would be great.


Okay thanks Shubhanmskatel, I'm making great progress, it looks like the owner is changing but some strange is happening. Here is the replay:

Created FactoryA: 0xE93a95c38272CdC2bB879488c69BB398dc41A22b

Created Project1 under FactoryA: 0x764C7a3d9714772324CEe21E80AbE881f51E0dC3

Created FactoryB: 0x5FbDB2315678afecb367f032d93F642f64180aa3

VALIDATE STARTING STATE:

  1. I run return all projects on FactoryA and it returns Project1.
  2. I run return all projects on FactoryB returns nothing.
  3. I run the owner function on Project1 to triple check and it returns FactoryA.

NOW THE TRANSFER:
I run the transferOwnership function on FactoryA, passing in Project1, and FactoryB as the new owner. no errors

I wait awhile, then run the above tests again in reverse.

  1. I run owner on Project1 it returns FactoryB – Positive result
  2. I run return all projects on FactoryA and it returns Project1. – wrong
  3. I run return all projects on FactoryB returns nothing. – wrong

So the owner on the Project is changing but when the new owner returns it's results nothing shows?

Best Answer

You can create a function in your Factory.sol contract which transfers the ownership of the said project. Since you are the owner of the Factory.sol contract, you can easily do that. Although you might need to use an interface to do that. Create an interface of the Project.sol contract, and hit transferOwnership from that interface.

Interface:

interface Project {
    function transferOwnership(address newOwner) external;
}

Factory.sol:

import "./ProjectInterface.sol";

contract Factory {
    function transferProjectOwnership(address projectAddress
    , address newOwner) public onlyOwner {
        Project(projectAddress).transferOwnership(newOwner);
    }
}