ethereumsoliditytruffle

Linking together smart contracts at deployment


DApp Architecture I'm building an application that is comprised of 3 smart contracts. The aim is to have the controller control the other two (contract A and B in the image). Previously, if I wanted to restrict the access to a smart contact, I would do it through a modifier Example:

  modifier onlyController {
     require(msg.sender == controller);
     _;
  }

At contact creation, the smart contract would set controller to be equal to whatever ethereum address I want to make these calls ( for example the address that deployed the smart contract). The issue is that now I want the address of the controller smart contract to be the controller (in the modifier). How would it be best to do this considering I want to deploy these set of smart contacts at the same time using truffle. What would be the best way to link these, so that the controller smart contract is only able to make calls to A and B. Moreover, how does the controller have to be implemented so that the user can call function in A and B going through the controller ( so the user calls a function in the controller, then the controller calls the corresponding function in A or B)?


Solution

  • You seem to be trying to implement a proxy pattern which is very common in other programming languages like java in such a way that you are providing very restricted access to certain classes in this case contracts.

    In Proxy design pattern both the proxy and the class to be restricted would be implementing the same interface! I do not know much about your smart contract details.

    Assuming Contract A and B implementing the same interface:

    Lets assume the interface is Base.

    interface Base{
       function getValue() external view returns (string); 
    }
    

    I need to provide controlled access to the ContractA and ContractB so only the controller could call. So lets create a modifier.

    contract ControlledAccess{
       
        address controller;
        
        constructor() public {
            controller = msg.sender;
        }
        
        modifier onlyController() {
            require(msg.sender == controller);
            _;
        }
        
    }
    

    Now ContractA and ContractB should implement interface Base and inherit ControlledAccess contract.

    contract ContractA is Base, ControlledAccess{
    
        function getValue() public view onlyController returns (string){
            return "Hi";
        }
    
    }
    
    contract ContractB is Base, ControlledAccess{
        
        function getValue() public view onlyController returns (string){
            return "Hello";
        }
    
    }    
    

    In order to set the controller address to be the ProxyController address, ProxyController itself should create these contracts in its constructor. As our ProxyController contract should be able to control more than 1 contract, I think mapping might be a good choice.

    contract ProxyController is Base{
    
        string public contractKey = "a";
        mapping(string => Base) base;
        
        constructor() public {
           base["a"]=new ContractA();
           base["b"]=new ContractB();
        }
        
        function setContractKey(string _contractKey) public{
            contractKey = _contractKey;    
        }
        
        function getValue() public view returns (string){
            return base[contractKey].getValue();
        }
        
    }
    

    so you can switch to A & B via setContractKey.

    Assuming there is no common functionalities between A and B:

    Remove the interface in the above example.Then implement something like this.

    contract ProxyController{
    
        ContractA a;
        ContractB b;
        
        constructor() public {
           a=new ContractA();
           b=new ContractB();
        }
        
        
        function AgetValue() public view returns (string){
            return a.getValue();
        }
        
        function BgetValue() public view returns (string){
            return b.getValue();
        }        
        
    }
    

    I tested this and it seems to be working fine. However I am not sure of other issues like performance, etc.