# Вызов dApp из dApp
Вызываемая функция dApp-скрипта может выполнять вложенные вызовы. Из dApp можно вызвать вызываемую функцию другого dApp или того же самого dApp, в том числе функция может вызвать сама себя. Вызов является синхронным. Вызванная функция возвращает значение, которое вызывающая функция может использовать.
Вызовы dApp из dApp доступны c момента активации фичи № 16 “Ride V5, dApp-to-dApp invocations”.
Вызов dApp из dApp осуществляется следующим образом:
- Пользователь отправляет транзакцию вызова скрипта или транзакцию в формате Ethereum, которая вызывает вызываемую функцию 1.
- Вызываемая функция 1 вызывает вызываемую функцию 2 с помощью нетерпеливой переменной и функции invoke или reentrantInvoke.
- Вызываемая функция 2 выполняется; вычисляются действия скрипта и возвращаемое значение.
- Возвращаемое значение присваивается нетерпеливой переменной. Последующие операции вызываемой функции 1 выполняются с учетом действий скрипта вызываемой функции 2 (как если бы эти действия были применены на блокчейне).
- В завершение действия скрипта вызываемых функций 2 и 1 применяются на блокчейне.
Особенности:
- Вызовы dApp из dApp могут быть вложенными.
- Все вызванные функции выполняются в рамках одной транзакции.
- Вызов dApp из dApp может содержать платежи, которые будут переведены с баланса исходного dApp на баланс вызываемого.
- Платежи, приложенные к вызову, могут использоваться как в действиях скрипта, так и в платежах, приложенных к вложенным вызовам.
Вызовы dApp из dApp появились с момента активации фичи № 16 “Ride V5, dApp-to-dApp invocations”.
# Условия
- Скрипты как исходного, так и вызываемого dApp используют Стандартную библиотеку версии 5 или выше.
- Если dApp вызывает сам себя, вызов не должен содержать платежи.
- Количество вызовов функций invoke или reentrantInvoke — не более 100 в одной транзакции.
- Общее количество действий скрипта
Issue
,Reissue
,Burn
,SponsorFee
, выполняемых всеми вызываемыми функциями в одной транзакции, — не более 30. - Общее количество действий скрипта
ScriptTransfer
,Lease
иLeaseCancel
, выполняемых всеми вызываемыми функциями в одной транзакции, — не более 100. - Общее количество действий скрипта
BinaryEntry
,BooleanEntry
,IntegerEntry
,StringEntry
,DeleteEntry
, выполняемых всеми вызываемыми функциями в одной транзакции, — не более 100. - Общее количество платежей, приложенных к вызовам dApp-скриптов, в одной транзакции, — не более 100.
- Общая сложность всех вызываемых функций и скриптов ассета, участвующих в транзакции, — не более 52 000 (если dApp-скрипт, который вызван первым, использует версию 6 Стандартной библиотеки). Сложность скрипта аккаунта-отправителя не учитывается в этом лимите.
# Нетерпеливая переменная
Для определения нетерпеливой переменной предназначено ключевое слово strict
. В отличие от ленивых переменных, определенных с помощью let
, значение нетерпеливой вычисляется немедленно, когда исполнение скрипта доходит до нее, то есть перед следующим выражением.
# Функции invoke и reentrantInvoke
invoke(dApp: Address|Alias, function: String, arguments: List[Any], payments: List[AttachedPayments]): Any
reentrantInvoke(dApp: Address|Alias, function: String, arguments: List[Any], payments: List[AttachedPayments]): Any
Параметр | Описание |
---|---|
dApp: Address|Alias | Адрес или псевдоним dApp, функция которого вызывается |
function: String|Unit | Имя вызываемой функции. unit — вызов функции по умолчанию |
arguments: List[Any] | Параметры вызываемой функции |
payments: List[AttachedPayment] | Платежи в пользу вызываемого dApp, не более 10 |
Пример:
strict z = invoke(dapp,foo,args,[AttachedPayment(unit,100000000)])
Возвращаемое значение имеет тип Any
, что означает любой допустимый тип. Извлечь из него конкретный тип можно с помощью макросов as[T]
и exactAs[T]
или оператора match ... case
, см. пример.
Функции invoke
и reentrantInvoke
отличаются только ограничением повторного вызова исходного dApp другими dApp в стеке вызова.
Подробнее в разделе Функция вызова dApp из dApp.
# Структура Invocation
В случае вызова dApp из dApp поля структуры Invocation, которую может использовать вызываемая функция, заполняются следующими значениями:
# | Название | Тип данных | Описание |
---|---|---|---|
1 | caller | Address | Адрес dApp, который вызвал функцию |
2 | callerPublicKey | ByteVector | Открытый ключ аккаунта dApp, который вызвал функцию |
3 | originCaller | Address | Адрес аккаунта, который отправил транзакцию |
4 | originCallerPublicKey | ByteVector | Открытый ключ аккаунта, который отправил транзакцию |
5 | payments | List[AttachedPayment] | Платежи, указанные в функции invoke или reentrantInvoke |
6 | transactionId | ByteVector | ID транзакции |
7 | fee | Int | Комиссия за транзакцию |
8 | feeAssetId | ByteVector|Unit | ID токена, в котором указана комиссия. Значение unit соответствует WAVES |
# Результат вызываемой функции
В Стандартной библиотеке версии 5 результат выполнения вызываемой функции представляет собой кортеж из двух элементов:
- Список действий скрипта.
- Возвращаемое значение, которое передается в вызывающую функцию.
Пример:
(
[
ScriptTransfer(i.caller,100,unit)
],
42
)
Подробнее в разделе Вызываемая функция.
# Обновление баланса и записей в хранилище данных аккаунта
Если функция, вызываемая с помощью invoke
или reentrantInvoke
, выполняет действия скрипта, то результаты этих действий доступны в вызывающей функции:
- Если вызываемая функция добавляет запись в хранилище данных аккаунта, то после вызова вызывающая функция может прочитать эту запись.
- Если вызываемая функция удаляет запись из хранилища данных аккаунта, то после вызова вызывающая функция не может прочитать эту запись.
- Если вызываемая функция выполняет действия с токенами (перевод, выпуск/довыпуск/сжигание и др.), то после вызова вызывающая функция при запросе балансов получает новые значения.
# Неуспешные транзакции
Если выполнение вызываемой функции завершается ошибкой или выбрасыванием исключения, транзакция вызова скрипта может быть отклонена или сохранена на блокчейне как неуспешная. Это зависит от того, превысила ли сложность выполненных вычислений порог для сохранения неуспешных транзакций, который сейчас равен 1000. Сложность суммируется по всем вызовам.
Рассмотрим пример: вызываемая функция 1 выполняет вычисления сложностью 800, затем вызывает вызываемую функцию 2, которая выполняет вычисления сложностью 300 и завершается ошибкой. Сложность 800 + 300 превышает порог, поэтому транзакция сохраняется как неуспешная и с отправителя взимается комиссия.
Если общая сложность выполненных вызываемых функций и скриптов ассета превысила ограничение 26 000, транзакция также сохраняется как неуспешная. Например, если общая сложность выполненных вызываемых функций составила 25 000 и в действиях скрипта есть смарт-ассет, у которого сложность скрипта составляет 1500.
В случае если транзакция неуспешна, никакие платежи и действия скрипта не применяются, даже если некоторые вызванные функции выполнены полностью. Единственное изменение на блокчейне, которое вносит неуспешная транзакция, — взимание комиссии с отправителя.