Advertencia: El uso de "llamadas" de bajo nivel debe evitarse siempre que sea posible

Estoy escribiendo mi propio contrato inteligente y quiero llamar a una función de un contrato que ya se implementó. El fragmento de mi código:

function myFunc(address _contractAddress, address _user, uint _price) onlyOwner {
        //... some code ...

        require(_contractAddress.call(bytes4(sha3("func(address, uint256)")), _user, _price));

        //... some code ...
    }

Estoy usando Remix IDE. Me muestra esta advertencia:

uso de "llamada": el uso de "llamada" de bajo nivel debe evitarse siempre que sea posible. Puede dar lugar a un comportamiento inesperado si el valor devuelto no se gestiona correctamente. Utilice llamadas directas especificando la interfaz del contrato llamado.

¿Cómo debo solucionar este problema? Delegatecall produce una advertencia similar. ¿Tal vez hay otras formas de llamar a la función de otro contrato?

Respuestas (2)

En las versiones más nuevas de solidity, puede usar contratos abstractos o interfaces .

  • Los contratos abstractos ahora necesitan la abstractpalabra clave para compilar correctamente y el virtualmodificador en funciones.
  • Las interfaces son mucho mejores para su propósito aquí, de hecho, para eso fueron diseñadas.

Estoy bastante seguro de que las interfaces están representadas en un nivel bajo por contratos abstractos con todas las funciones virtuales externas y sin campos de datos internos.

MiContrato.sol:

// SPDX-License-Identifier: Unlicense                                                                               
pragma solidity >0.8.5;

abstract contract OtherContract {
    function otherMethod(address _to, uint _price) external virtual;
}

interface OtherContractInterface {
    function otherMethod(address _to, uint _price) external;
}

contract MyContract {
    uint public unitPrice = 100;

    function myMethod(address _destination, uint _count) external {
        // _destination is a contract that implements OtherContract

        // this uses the interface.
        OtherContractInterface oci = OtherContractInterface(_destination);
        oci.otherMethod(address(this), _count * unitPrice);

        // this code uses the abstract contract
        OtherContract oc = OtherContract(_destination);
        oc.otherMethod(address(this), _count * unitPrice);
    }
}

Si conoce el método para llamar, puede usar un contrato abstracto

contract OtherContract {
    function otherMethod(address _to, uint _price);
}

contract MyContract {
    uint public unitPrice = 100;

    function myMethod(address _destination, uint _count) {
        // _destination is a contract that implements OtherContract
        OtherContract oc = OtherContract(_destination);
        // call method from other contract
        oc.otherMethod(address(this), _count * unitPrice);
    }
}
mis contratos están en archivos separados, así que esto no funcionará.
Si los contratos están en archivos separados, puede importarlos o crear un archivo que solo tenga las declaraciones de los métodos, un "contrato abstracto" en lenguaje sólido.
Importar contrato es lo mismo que declararlo arriba en el mismo archivo. Mi situación es que estos contratos se implementan por separado.
Es una técnica común utilizada en crowdsales. Tienes dos contratos desplegados de forma independiente Crowdsale y CrowdsaleToken. Crowdsale necesita acceso a CrowdsaleToken, pero si lo importa en el archivo Crowdsale.sol, aumentará el código de bytes. Por lo tanto, crea un token de contrato abstracto sin implementación, solo la declaración de métodos y lo importa desde Crowdsale.