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.- π¦ Rust
- π JavaScript
- π Python
- πΉ GO
- low_level.rs
- high_level.rs
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.- π¦ Rust
- π JavaScript
- π Python
- πΉ GO
- low_level.rs
- high_level.rs
Promises
Cross-contract calls work by creating two promises in the network:- A promise to execute code in the external contract -
Promise.create - Optional: A promise to call another function with the result -
Promise.then
- The address of the contract you want to interact with
- The function that you want to execute
- The arguments to pass to the function
- The amount of GAS to use (deducted from the attached Gas)
- The NEAR deposit to attach (deducted from your contractβs balance)
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:
- π¦ Rust
- π JavaScript
- πΉ GO
- High Level API
- Low Level API
Concatenating Promises
Concatenating Promises
β
You can concatenate 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 can join promises: (P1.and(P2)).then(P3): P1 and P2 execute in parallel, after they finish P3 will execute and have access to both their resultsβ You cannot return a joint promise without a callback: return P1.and(P2) is invalid, you need to add a .then()β You cannot join promises within a then: P1.then(P2.join([P3])) is invalidβ You cannot use a then within a then: P1.then(P2.then(P3)) is invalidIf 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 statusAttaching gas
Each promise in a cross-contract call needs enough gas to execute. This includes the external call and any callback you attach with.then.
Rust
Rust
In Rustβs high level API, The high level API also exposes The default unused gas weight is
The runtime reserves
with_static_gas(gas) reserves a fixed amount of gas for a promise:with_unused_gas_weight(weight), which gives a promise a proportional share of the gas left after all static gas has been reserved.1. Set the weight to 0 when you want a promise to receive only its static gas reservation.For example, if 100 Tgas is left when you create two promises:| Promise | Static gas | Unused gas weight |
|---|---|---|
| A | 20 Tgas | 1 |
| B | 10 Tgas | 3 |
30 Tgas as static gas, then distributes the remaining 70 Tgas by weight:- A gets
20 + (1/4) * 70 = 37.5 Tgas - B gets
10 + (3/4) * 70 = 62.5 Tgas
Promise::new(...).function_call(...)) takes a fixed gas value. It behaves like with_static_gas and does not use unused gas weights.JavaScript / TypeScript
JavaScript / TypeScript
In JavaScript and TypeScript, pass the gas amount to There is no unused gas weight equivalent. The gas value you pass is the fixed amount attached to that promise.
functionCall. Do this for both the external call and the callback:Python
Python
In Python, pass gas when creating the promise call. The exact helper depends on whether you are using Callbacks need their own gas reservation. Do not assume unused gas from the external call will automatically be available to the callback.
CrossContract or lower level promise helpers, but the idea is the same: reserve gas for each receipt that must execute.Go
Go
In Go, pass gas when creating the function call promise. The callback routed through There is no unused gas weight equivalent. Treat the gas value as the fixed amount reserved for the promise.
.Then() still needs enough gas to execute.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.- π¦ Rust
- π JavaScript
- π Python
- πΉ GO
Callback with always executeWe 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
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:- Refund the predecessor if needed: If the contract attached NEAR to the call, the funds are now back in the contractβs account
- 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
Calling Multiple Functions on the 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.- π¦ Rust
- π JavaScript
- π Python
- πΉ GO
Calling Multiple Functions on 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.- π¦ Rust
- π JavaScript
- π Python
- πΉ GO
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
- Make sure you donβt leave the contract in a exploitable state between the call and the callback.
- Manually rollback any changes to the state in the callback if the external call failed.