NEAR Smart Contracts · 1 min read
Promises and Callbacks
Schedule a cross-contract call with Promise, attach gas, and handle the result in a private callback.
A contract cannot call another contract synchronously. Accounts can live on different shards, so NEAR turns the call into a receipt. You create a Promise, the runtime runs the other contract later, and if you need the result you chain a callback with .then(...).
The pattern
- Build a promise to the other contract (
ext_contractin Rust, orPromisein JS). - Attach static gas for that leg with
with_static_gas. - Chain
.then(Self::ext(current).callback(...))and attach gas for the callback too. - Mark the callback
#[private]so only the contract can call it. - Read the result with
#[callback_result].
rust
use near_sdk::{env, ext_contract, near, AccountId, Gas, NearToken, Promise, PromiseError};
#[ext_contract(ext_ft)]
trait FungibleToken {
fn ft_balance_of(&self, account_id: AccountId) -> NearToken;
}
#[near]
impl Contract {
pub fn snapshot_balance(&mut self, token: AccountId, who: AccountId) -> Promise {
ext_ft::ext(token)
.with_static_gas(Gas::from_tgas(10))
.ft_balance_of(who.clone())
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas::from_tgas(5))
.on_balance(who),
)
}
#[private]
pub fn on_balance(
&mut self,
who: AccountId,
#[callback_result] result: Result<NearToken, PromiseError>,
) -> String {
match result {
Ok(amount) => {
self.last = amount;
format!("{who} has {amount}")
}
Err(_) => "Balance lookup failed".to_string(),
}
}
}Rules that bite
- If the other contract fails, your earlier state writes are not rolled back. Undo them in the callback.
- Budget gas for both legs. A typical split is 10–30 Tgas on the outgoing call and 5–10 Tgas on the callback.
- Deposits you attach with
with_attached_depositare spent even if the callee panics, unless you refund in the callback. #[private]is not a suggestion. Without it, anyone could spoof the callback.
The holder-only Cross-Contract Calls lesson applies this pattern to gate a feature on a Retr0 Academy diploma.
Exercise
Write (on paper or in a file) a join_club method that calls verify_diploma on diploma.retr0school.testnet and a #[private] callback that only records the member when the result is Ok(Some(_)).
Saved in this browser.