Содержание

Функции в Solidity

View-функции

Функции могут быть объявлены, как view, в том случае, если они не должны изменять состояние.

Изменять состояние — это означает:

  1. Записывать глобальные переменные.
  2. Вызывать события.
  3. Создавать другие контракты.
  4. Использовать selfdestruct.
  5. Отправлять эфир через вызовы.
  6. Вызывать функцию, которая не view и не pure.
  7. Использовать низкоуровневые вызовы.
  8. Использовать встроенную сборку, которая содержит определенные коды операций.
pragma solidity ^0.4.16;

contract C {
    function f(uint a, uint b) public view returns (uint) {
        return a * (b + 42) + now;
    }
}

Примечание

constant — это псевдоним для view.

Примечание

Геттер-методы указываются, как view.

Предупреждение

Компилятор еще не применяет то, что метод view не изменяет состояние.

Pure-функции

Функции могут быть объявлены pure, не читают и не изменяют состояние.

Читать состояние — это означает:

  1. Чтение глобальных переменных.
  2. Иметь доступ к this.balance или <address>.balance.
  3. Иметь доступ к любому члену block, tx, msg (за исключение msg.sig и msg.data).
  4. Вызывать любую функцию, которая не pure.
  5. Использовать встроенную сборку, которая содержит определенные коды операций.
pragma solidity ^0.4.16;

contract C {
    function f(uint a, uint b) public pure returns (uint) {
        return a * (b + 42);
    }
}

Предупреждение

Компилятор еще не применяет то, что метод pure не читает состояние.

Fallback-функция

Смарт-контракт может иметь только одну функцию без названия. Эта функция не может иметь аргументы и не может что-либо возвращать. Она выполняется по вызову контракта, если ни одна из других функций не соответствует указанному идентификатору функции (или если данные не были предоставлены вообще).

Кроме того, эта функция выполняется всякий раз, когда контракт получает простой эфир (без данных). Вдобавок, чтобы получить эфир, fallback-функция должна быть отмечена payable. Если такой функции нет, то контракт не может получить эфир через регулярные транзакции.

В таком контексте обычно доступно очень мало газа для вызова функции (точнее, 2300 газа), поэтому важно создавать fallback-функции настолько дешевыми, насколько это возможно. Заметьте, что количество газа, требуемого за транзакцию (в отличие от внутреннего вызова), которая запускает fallback-функцию намного выше, потому что за каждую транзакцию взимается дополнительно 21000 газа или более для таких вещей, как проверка подписи.

В частности, следующие операции будут расходовать больше газа, чем вознаграждение, предоставляемое fallback-функцией:

  • Запись в хранилище
  • Создание контракта
  • Вызов external-функции, которая расходует большое количество газа
  • Отправка эфира

Необходимо тщательно проверить fallback-функцию перед деплоем контракта, чтобы быть уверенным, что стоимость выполнения меньше 2300 газа.

Примечание

Несмотря на то, что функция возврата не может иметь аргументы, все равно можно использовать msg.data для извлечения любой полезной нагрузки, поставляемой с вызовом.

Предупреждение

Контракты, которые получают эфир напрямую (без вызова функции, а с использованием send или transfer) и не объявляют fallback-функцию, генерируют исключение и отправляют эфир обратно (это было иначе до Solidity v0.4.0). Таким образом, если нужно, чтобы контракт получал эфир, необходимо реализовать fallback-функцию.

Предупреждение

Контракт без fallback-функции может получить эфир в качестве получателя coinbase транзации (также известной, как вознаграждение за майнинг блока) или в качестве предназначения selfdestruct. Контракт не может реагировать на такие передачи эфира, и, таким образом, не может отменить их. Это работает на уровне EVM, и Solidity не может обойти это. Это также означает, что this.balance может быть выше, чем сумма некоторого ручного учета, реализованного в контракте (то есть иметь счетчик обновленной fallback-функции).

pragma solidity ^0.4.0;

contract Test {
    // Это функция, которая вызывается для всех messages,
    // отправленных на этот контракт (нет другой функции).
    // Отправка эфира на этот контракт вызовет исключение,
    // потому что у fallback-функции отсутствует модификатор
    // `payable`.
    function() public { x = 1; }
    uint x;
}


// Этот контракт содержит весь эфир, отправленный на него
// без возможности возврата.
contract Sink {
    function() public payable { }
}

contract Caller {
    function callTest(Test test) public {
        test.call(0xabcdef01); // хэша нет,
        // в результате test.x станет == 1.

        // Следующий код не скомпилируется, но даже
        // если кто-то бы отправил эфир на контракт,
        // транзакция провалится и отклонит эфир.
        //test.send(2 ether);
    }
}

Перегрузка функций

Контракт может иметь несколько функций с тем же именем, но с разными аргументами. Это также относится к унаследованным функциям. Следующий пример демонстрирует перегрузку функции f в контракте A.

pragma solidity ^0.4.16;

contract A {
    function f(uint _in) public pure returns (uint out) {
        out = 1;
    }

    function f(uint _in, bytes32 _key) public pure returns (uint out) {
        out = 2;
    }
}

Перегруженные функции также представлены во внешнем интерфейсе. Ошибка, если 2 внешне видимые функции отличаются по типам Solidity, а не по своим внешним типам.

// Этот код не скомпилируется
pragma solidity ^0.4.16;

contract A {
    function f(B _in) public pure returns (B out) {
        out = _in;
    }

    function f(address _in) public pure returns (address out) {
        out = _in;
    }
}

contract B {
}

Обе f функции перегрузки превышают допустимый тип адреса для ABI, хотя они считаются разными внутри Solidity.

Решение перегрузки и сопоставление аргументов

Перегруженные функции отбираются путем сопоставления объявлений функций в текущей области с аргументами, входящими в вызов функции. Функции становятся кандидатами для перегрузки, если все аргументы могут быть неявно преобразованы в ожидаемые типы. Если это не один кандидат, то разрешение не выполняется.

Примечание

Возвращаемые параметры не учитываются при разрешении перегрузки.

pragma solidity ^0.4.16;

contract A {
    function f(uint8 _in) public pure returns (uint8 out) {
        out = _in;
    }

    function f(uint256 _in) public pure returns (uint256 out) {
        out = _in;
    }
}

Вызов f(50) создаст ошибку типа, поскольку 250 может быть неявно преобразован как в типы uint8, так и uint256. С другой стороны f(256) разрешило бы перегрузку f(uint256), поскольку 256 не может быть неявно преобразовано в uint8.

Материал был полезен? Поделитесь в соц. сетях:
Логотип echain.ru

Добавить комментарий

Ваш e-mail не будет опубликован.