0

I'm looking for help to build an intermediate contract using delegatecall. My challenge would be with 3 contracts.

I need contract A to be the owner of Contract B, and contract B to have a transferownership function to transfer to another address but to be only owner, the role of contract C will be as an intermediary, contract A must make a call to C and the C delegatecall to the B.

The function to be performed on B is onlyowner, so only A could do it, but it needs an intermediate contract.

I was trying with this test:

// SPDX-License-Identifier: MIT
// ContratoA.sol
pragma solidity ^0.8.0;

import "./ContratoC.sol";  // Importe o ContratoC aqui

contract ContratoA {
    address public owner;
    address public contratoB;
    address public contratoC;

    modifier onlyOwner() {
        require(msg.sender == owner, "Somente o proprietario pode chamar esta funcao ESTOUNOCONTRATOA");
        _;
    }

    event ContratoBIncluido(address indexed contratoB);
    event ContratoCIncluido(address indexed contratoC);
    event OwnershipTransferAttempt(address indexed newOwner, bool success);

    constructor() {
        owner = msg.sender;
    }

    function incluirContratoB(address _enderecoContratoB) public onlyOwner {
        contratoB = _enderecoContratoB;
        emit ContratoBIncluido(contratoB);
    }

    function incluirContratoC(address _enderecoContratoC) public onlyOwner {
        contratoC = _enderecoContratoC;
        emit ContratoCIncluido(contratoC);
    }

    // Função para chamar transferOwnershipContratoB no Contrato C
    function transferOwnershipContratoB(address newOwner) public onlyOwner {
        require(contratoC != address(0), "Endereco do Contrato C nao configurado");

        emit OwnershipTransferAttempt(newOwner, false);

        ContratoC contratoCInstance = ContratoC(contratoC);
        contratoCInstance.transferOwnershipContratoB(newOwner);

        emit OwnershipTransferAttempt(newOwner, true);
    }
}


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract ContratoB {
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner, "Somente o proprietario pode chamar essa funcaoESTOUNOCONTRATOB");
        _;
    }

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor() {
        owner = msg.sender;
    }

    function transferOwnership(address newOwner) public onlyOwner {
        emit OwnershipTransferred(owner, newOwner);

        require(newOwner != address(0), "Endereco invalido");

        address previousOwner = owner;
        owner = newOwner;

        emit OwnershipTransferred(previousOwner, newOwner);
    }

    function getOwner() public view returns (address) {
        return owner;
    }
}


// SPDX-License-Identifier: MIT

// ContratoC.sol
pragma solidity ^0.8.0;

import "./ContratoB.sol";

contract ContratoC {
    address public contratoA;

    event BeforeDelegateCall(address newOwner);
    event AfterDelegateCall(bool success, bytes data);
    event DelegateCallData(bytes data); // Declaração do evento adicionada

    modifier onlyContratoA() {
        require(msg.sender == contratoA, "Somente o Contrato A pode chamar essa funcao CONTRATOC");
        _;
    }

    constructor() {
        // ContratoA ainda não está definido ao criar ContratoC
    }

    function setContratoA(address _contratoA) public {
        require(_contratoA != address(0), "Endereco do Contrato A invalido");
        contratoA = _contratoA;
    }

    function transferOwnershipContratoB(address newOwner) public onlyContratoA {
        require(contratoA != address(0), "Endereco do Contrato A nao configurado");

        emit BeforeDelegateCall(newOwner);

        (bool success, bytes memory data) = contratoA.delegatecall(
            abi.encodeWithSignature("transferOwnership(address)", newOwner)
        );

        // Adiciona logs adicionais para depuração
        emit AfterDelegateCall(success, data);
        emit DelegateCallData(data);

        require(success, "Chamada para o Contrato A falhou");
    }

    // ...
}

1 Answer 1

0

Since ContratoA is owner of ContratoB, then only ContratoA can call contractB.transferOwnership(). There's no way for ContratoC to call contractB.transferOwnership().

You can make that ContratoA delegatecalls some function in ContratoC. Then that function will execute as if it were ContratoA, and it may call contractB.transferOwnership().

contract ContratoA {
    address public owner;
    address public contratoB;
    address public contratoC;

    // ...

    function foo(address newOnwer) public {
        contratoC.delegatecall(abi.encodeWithSignature("bar(address)", newOwner));
    }
}

// Since ContratoA delegatecalls ContratoC both have to be compatible
// e.g. same variables in the same order, etc.
contract ContratoC {
    address public owner;
    address public contratoB;
    address public contratoC;

    // ..

    function bar(address newOnwer) public {
        ContratoC(contractB).transferOwnership(newOwner);
    }
}

PD: I didn't have time to test so take the code as an idea. It is missing error checks.

2
  • So let's suppose that the Contract is already published and does not have the delegatecall nor has a specific call been foreseen to call the transferownership function in contract B, the delegatecall will not work right? Do you believe there is any solution? I have a real problem with this case I went through. Commented Dec 8, 2023 at 18:08
  • @LeandroLoures Unfortunately, in that conditions then only ContractA can call ContractB.transferOwnership. Check this answer that explains how delegatecall works ethereum.stackexchange.com/questions/3667/….
    – Ismael
    Commented Dec 10, 2023 at 22:54

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.