Предупреждение: по возможности следует избегать использования низкоуровневого «вызова».

Я пишу свой собственный смарт-контракт и хочу вызвать функцию из уже развернутого контракта. Фрагмент моего кода:

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

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

        //... some code ...
    }

Я использую Remix IDE. Он показывает мне это предупреждение:

использование «вызова»: по возможности следует избегать использования «вызова» низкого уровня. Это может привести к неожиданному поведению, если возвращаемое значение не обрабатывается должным образом. Используйте прямые вызовы, указав интерфейс вызываемого контракта.

Как мне решить эту проблему? Delegatecall выдает аналогичное предупреждение. Может быть, есть другие способы вызова функции другого контракта?

Ответы (2)

В новых версиях Solidity можно использовать либо абстрактные контракты , либо интерфейсы .

  • Абстрактные контракты теперь нуждаются в abstractключевом слове для правильной компиляции и virtualмодификаторе в функциях.
  • Интерфейсы здесь намного лучше подходят для ваших целей, собственно, для этого они и были созданы.

Я почти уверен, что интерфейсы представлены на низком уровне абстрактными контрактами со всеми внешними виртуальными функциями и без внутренних полей данных.

MyContract.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);
    }
}

Если вы знаете метод вызова, вы можете использовать абстрактный контракт

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);
    }
}
мои контракты находятся в отдельных файлах, так что это не сработает.
Если контракты находятся в отдельных файлах, вы можете импортировать их или создать файл, содержащий только объявления методов, «абстрактный контракт» на языке солидности.
Импорт контракта аналогичен его объявлению выше в том же файле. Моя ситуация заключается в том, что эти контракты развертываются отдельно
Это распространенная техника, используемая в краудсейлах. У вас есть два независимо развернутых контракта Crowdsale и CrowdsaleToken. Crowdsale требуется доступ к CrowdsaleToken, но если вы импортируете его в файл Crowdsale.sol, это увеличит байт-код. Таким образом, вы создаете токен абстрактного контракта без реализации, только объявление методов и импортируете его из Crowdsale.