# dApp-to-dApp Invocation Functions
Name | Description | Complexity |
---|---|---|
invoke | Invokes a dApp callable function, with reentrancy restriction | 75 |
reentrantInvoke | Invokes a dApp callable function, without reentrancy restriction | 75 |
# invoke
Invokes a dApp callable function, with reentrancy restriction.
invoke(dApp: Address|Alias, function: String, arguments: List[Any], payments: List[AttachedPayments]): Any
Any
means any valid type. You can extract a particular type from it using as[T]
and exactAs[T]
macros or the match ... case
operator, see the example.
The invoke
function can be used by a callable function of a dApp script, but not by a verifier function, account script or asset script.
Via the invoke
function, the callable function can invoke a callable function of another dApp, or another callable function of the same dApp, or even itself, and then use the invocation results in subsequent operations. For details, see the dApp-to-dApp Invocation article.
💡 To ensure executing callable functions and applying their actions in the right order, initialize a strict variable by the return value of an invoke
function.
The invocation can contain payments that will be transferred from the balance of the parent dApp to the balance of the invoked dApp. Payments are forbidden if the dApp invokes itself.
If a payment token is a smart asset, the asset script verifies the invoke
as if it was InvokeScriptTransaction with the following fields:
dApp
,payments
,function
,args
indicated in theinvoke
function;sender
,senderPublicKey
of the dApp that performs the invocation;id
,timestamp
,fee
,feeAssetId
indicated in the original Invoke Script transaction;version
= 0;
If the asset script denies the action, the Invoke Script transaction is either discarded or saved on the blockchain as failed, see the Transaction Validation.
# Reentrancy Restriction
The invocation stack generated by the invoke
function must not contain invocations of the parent dApp after invocation of another dApp.
Let the parent dApp A invokes dApp B using the invoke
function. Regardless of whether dApp B uses invoke
or reentrantInvoke
, the following invocation stacks will fail:
→ dApp A
→ dapp B
→ dApp A
→ dApp A
→ dapp B
→ dApp C
→ dApp A
The following invocation stacks are valid:
→ dApp A
→ dapp A
→ dapp A
→ dApp N
→ dapp A
→ dApp A
→ dapp N
→ dapp A
→ dapp B
→ dapp B
→ dapp A
→ dapp C
# Parameters
Parameter | Description |
---|---|
dApp: Address|Alias | Address or alias of a dApp to invoke |
function: String|Unit | Name of a callable function. unit for a default function invocation |
arguments: List[Any] | Parameters of a callable function |
payments: List[AttachedPayment] | Payments to transfer from the parent dApp to the invoked dApp, up to 10 |
# Example
A user sends an Invoke Script transaction that invokes the callable function foo
of dApp1.
The foo
function invokes the bar
function of dApp2 passing the number a
and attaching a payment of 1 XTN.
The bar
function transfers 1 WAVES to dApp1 and returns the doubled number a
.
The foo
function writes to dApp1 data storage:
- the value returned by
bar
, - the new balance of dApp2 (reduced by 1 WAVES transferred to dApp1).
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
Invokes a dApp callable function. The only difference from the invoke function is that there is no reentrancy restriction for the parent dApp that uses reentrantInvoke
.
However, if the parent dApp is invoked again and this time uses the invoke
function, the parent dApp cannot be invoked again in this invocation stack.
For example, the invocation stack
→ dApp A
→ dapp B
→ dApp A
→ dApp C
→ dApp A
- is valid if dApp A invokes both dApp B and dApp C via the
reentrantInvoke
function; - fails if dApp A invokes dApp B via the
reentrantInvoke
function and invokes dApp C via theinvoke
function.
reentrantInvoke(dApp: Address|Alias, function: String, arguments: List[Any], payments: List[AttachedPayments]): Any