# Функции вызова dApp из dApp
Название | Описание | Сложность |
---|---|---|
invoke | Вызывает вызываемую функцию dApp, c ограничением на повторные вызовы исходного dApp | 75 |
reentrantInvoke | Вызывает вызываемую функцию dApp, без ограничения на повторные вызовы исходного dApp | 75 |
# invoke
Вызывает вызываемую функцию dApp, с ограничением на повторные вызовы.
invoke(dApp: Address|Alias, function: String, arguments: List[Any], payments: List[AttachedPayments]): Any
Any
означает любой допустимый тип. Извлечь из него конкретный тип можно с помощью макросов as[T]
и exactAs[T]
или оператора match ... case
, см. пример.
Функция invoke
может использоваться только вызываемой функцией dApp-скрипта, но не функцией-верификатором, скриптом аккаунта или скриптом ассета.
С помощью функции invoke
вызываемая функция может вызвать вызываемую функцию другого dApp или того же самого dApp, в том числе сама себя, а затем использовать результаты вызова в дальнейших вычислениях. Подробнее в разделе Вызов dApp из dApp.
💡 Чтобы гарантировать порядок выполнения вызываемых функций и применения действий скрипта, присвойте возвращаемое значение invoke
нетерпеливой переменной.
Вызов может содержать платежи, которые будут переведены с баланса исходного dApp на баланс вызываемого. Платежи запрещены, если dApp вызывает сам себя.
Если токен в платеже является смарт-ассетом, то скрипт ассета верифицирует вызов invoke
как InvokeScriptTransaction с полями:
dApp
,payments
,function
,args
— значения, указанные в функцииinvoke
;sender
,senderPublicKey
— параметры dApp, который вызвал функцию;id
,timestamp
,fee
,feeAssetId
— как в транзакции вызова скрипта;version
= 0.
Если скрипт ассета отклоняет действие, то транзакция, которая вызвала скрипт dApp, либо отбрасывается, либо сохраняется на блокчейне как неуспешная, см. раздел Валидация транзакций.
# Ограничение повторных вызовов
Стек вызовов, порожденный функцией invoke
, не должен содержать вызовы исходного dApp после вызова другого dApp.
Пусть исходный dApp A вызывает dApp B c помощью функции invoke
. Независимо от того, какую функцию использует dApp B — invoke
или reentrantInvoke
, следующие последовательности вызовов завершатся ошибкой:
→ dApp A
→ dapp B
→ dApp A
→ dApp A
→ dapp B
→ dApp C
→ dApp A
Следующие последовательности вызовов допустимы:
→ dApp A
→ dapp A
→ dapp A
→ dApp N
→ dapp A
→ dApp A
→ dapp N
→ dapp A
→ dapp B
→ dapp B
→ dapp A
→ dapp C
# Параметры
Параметр | Описание |
---|---|
dApp: Address|Alias | Адрес или псевдоним dApp, функция которого вызывается |
function: String|Unit | Имя вызываемой функции. unit — вызов функции по умолчанию |
arguments: List[Any] | Параметры вызываемой функции |
payments: List[AttachedPayment] | Платежи в пользу вызываемого dApp, не более 10 |
# Пример
Пользователь с помощью транзакции вызова скрипта вызывает функцию foo
dApp1.
Функция foo
вызывает функцию bar
dApp2, передавая число a
и прикладывая платеж 1 XTN.
Функция bar
переводит dApp1 1 WAVES и возвращает удвоенное число a
.
Функция foo
записывает в хранилище данных dApp1:
- значение, возвращенное функцией
bar
. - новый баланс dApp2 (уменьшенный на 1 WAVES, переведенный функцией
bar
).
dApp1:
{-# STDLIB_VERSION 8 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}
@Callable(i)
func foo(dapp2: String, a: Int, key1: String, key2: String) = {
strict res = invoke(addressFromStringValue(dapp2),"bar",[a],[AttachedPayment(base58'DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p',1000000)])
match res {
case r : Int =>
(
[
IntegerEntry(key1, r),
IntegerEntry(key2, wavesBalance(addressFromStringValue(dapp2)).regular)
],
unit
)
case _ => throw("Incorrect invoke result")
}
}
dApp2:
{-# STDLIB_VERSION 8 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}
@Callable(i)
func bar(a: Int) = {
(
[
ScriptTransfer(i.caller, 100000000, unit)
],
a*2
)
}
# reentrantInvoke
Вызывает вызываемую функцию dApp. Отличается от функции invoke только отсутствием ограничения на повторные вызовы исходного dApp, который использовал reentrantInvoke
.
Однако, если исходный dApp был вызван повторно и на этот раз использовал функцию invoke
, то в этом стеке вызовов нельзя вызвать исходный dApp еще раз.
Например, последовательность вызовов
→ dApp A
→ dapp B
→ dApp A
→ dApp C
→ dApp A
- допустима, если dApp A вызывает и dApp B, и dApp С с помощью функции
reentrantInvoke
; - завершается ошибкой, если dApp A вызывает dApp B с помощью функции
reentrantInvoke
, а dApp С — с помощью функцииinvoke
.
reentrantInvoke(dApp: Address|Alias, function: String, arguments: List[Any], payments: List[AttachedPayments]): Any