Skip to main content

Cross-Contract Calls

Your contract can interact with other deployed contracts, querying information and executing functions on them.

Since NEAR is a sharded blockchain, its cross-contract calls behave differently than calls do in other chains. In NEAR. cross-contract calls are asynchronous and independent.

Cross-Contract Calls are Independent

You will need two independent functions: one to make the call, and another to receive the result

Cross-Contract Calls are Asynchronous

There is a delay between the call and the callback execution, usually of 1 or 2 blocks. During this time, the contract is still active and can receive other calls.


Snippet: Querying Informationโ€‹

While making your contract, it is likely that you will want to query information from another contract. Below, you can see a basic example in which we query the greeting message from our Hello NEAR example.


Snippet: Sending Informationโ€‹

Calling another contract passing information is also a common scenario. Below you can see a function that interacts with the Hello NEAR example to change its greeting message.


Promisesโ€‹

Cross-contract calls work by creating two promises in the network:

  1. A promise to execute code in the external contract (Promise.create)
  2. Optional: A promise to call another function with the result (Promise.then)

Both promises will contain the following information:

  • The address of the contract you want to interact with
  • The function that you want to execute
  • The (encoded) arguments to pass to the function
  • The amount of GAS to use (deducted from the attached Gas)
  • The amount of NEAR to attach (deducted from your contractโ€™s balance)
tip

The callback can be made to any contract. Meaning that the result could potentially be handled by another contract


Creating a Cross Contract Callโ€‹

To create a cross-contract call with a callback, create two promises and use the .then method to link them:

NearPromise.new("external_address").functionCall("function_name", JSON.stringify(arguments), DEPOSIT, GAS)
.then(
// this function is the callback
NearPromise.new(near.currentAccountId()).functionCall("callback_name", JSON.stringify(arguments), DEPOSIT, GAS)
);
info

If a function returns a promise, then it will delegate the return value and status of transaction execution, but if you return a value or nothing, then the Promise result will not influence the transaction status

caution

The Promises you are creating will not execute immediately. In fact, they will be queued in the network an:

  • The cross-contract call will execute 1 or 2 blocks after your function finishes correctly.

Callback Functionโ€‹

If your function finishes correctly, then eventually your callback function will execute. This will happen whether the external contract fails or not.

In the callback function you will have access to the result, which will contain the status of the external function (if it worked or not), and the values in case of success.

Callback with always execute

We repeat, if your function finishes correctly, then your callback will always execute. This will happen no matter if the external function finished correctly or not

warning

Always make sure to have enough Gas for your callback function to execute

tip

Remember to mark your callback function as private using macros/decorators, so it can only be called by the contract itself


What happens if the function I call fails?โ€‹

If the external function fails (i.e. it panics), then your callback will be executed anyway. Here you need to manually rollback any changes made in your contract during the original call. Particularly:

  1. Refund the predecessor if needed: If the contract attached NEAR to the call, the funds are now back in the contract's account
  2. Revert any state changes: If the original function made any state changes (i.e. changed or stored data), you need to manually roll them back. They won't revert automatically
warning

If your original function finishes correctly then the callback executes even if the external function panics. Your state will not rollback automatically, and $NEAR will not be returned to the signer automatically. Always make sure to check in the callback if the external function failed, and manually rollback any operation if necessary.


Concatenating Functions and Promisesโ€‹

โœ… Promises can be concatenate using the .and operator: P1.and(P2).and(P3).then(P4): P1, P2, and P3 execute in parallel, after they finish, P4 will execute and have access to all their results

โ›” You cannot return a joint promise without a callback: return P1.and(P2) is invalid since it misses the then

โœ… You can concatenate then promises: P1.then(P2).then(P3): P1 executes, then P2 executes with the result of P1, then P3 executes with the result of P2

โ›” You cannot use an and within a then: P1.then(P2.and(P3)) is invalid

โ›” You cannot use a then within a then: P1.then(P2.then(P3)) is invalid


Multiple Functions, Same Contractโ€‹

You can call multiple functions in the same external contract, which is known as a batch call.

An important property of batch calls is that they act as a unit: they execute in the same receipt, and if any function fails, then they all get reverted.

tip

Callbacks only have access to the result of the last function in a batch call


Multiple Functions: Different Contractsโ€‹

You can also call multiple functions in different contracts. These functions will be executed in parallel, and do not impact each other. This means that, if one fails, the others will execute, and NOT be reverted.

tip

Callbacks have access to the result of all functions in a parallel call


Security Concernsโ€‹

While writing cross-contract calls there is a significant aspect to keep in mind: all the calls are independent and asynchronous. In other words:

  • The function in which you make the call and function for the callback are independent.
  • There is a delay between the call and the callback, in which people can still interact with the contract

This has important implications on how you should handle the callbacks. Particularly:

  1. Make sure you don't leave the contract in a exploitable state between the call and the callback.
  2. Manually rollback any changes to the state in the callback if the external call failed.

We have a whole security section dedicated to these specific errors, so please go and check it.

warning

Not following these basic security guidelines could expose your contract to exploits. Please check the security section, and if still in doubt, join us in Discord.

Was this page helpful?