# Cross-canister

Examples:

- async_await
- bitcoin
- composite_queries
- cross_canister_calls
- cycles
- ethereum_json_rpc
- func_types
- heartbeat
- inline_types
- ledger_canister
- management_canister
- outgoing_http_requests
- threshold_ecdsa
- rejections
- timers
- tuple_types
- whoami

Canisters are generally able to call the query or update methods of other canisters in any subnet. We refer to these types of calls as cross-canister calls.

A cross-canister call begins with a definition of the canister to be called.

Imagine a simple canister called `token_canister`:

    import {
        Canister,
        ic,
        nat64,
        Opt,
        Principal,
        StableBTreeMap,
        update
    } from 'azle';

    let accounts = StableBTreeMap<Principal, nat64>(0);

    export default Canister({
        transfer: update([Principal, nat64], nat64, (to, amount) => {
            const from = ic.caller();

            const fromBalance = getBalance(accounts.get(from));
            const toBalance = getBalance(accounts.get(to));

            accounts.insert(from, fromBalance - amount);
            accounts.insert(to, toBalance + amount);

            return amount;
        })
    });

    function getBalance(accountOpt: Opt<nat64>): nat64 {
        if ('None' in accountOpt) {
            return 0n;
        } else {
            return accountOpt.Some;
        }
    }

Now that you have the canister definition, you can import and instantiate it in another canister:

    import { Canister, ic, nat64, Principal, update } from 'azle';
    import TokenCanister from './token_canister';

    const tokenCanister = TokenCanister(
        Principal.fromText('r7inp-6aaaa-aaaaa-aaabq-cai')
    );

    export default Canister({
        payout: update([Principal, nat64], nat64, async (to, amount) => {
            return await ic.call(tokenCanister.transfer, {
                args: [to, amount]
            });
        })
    });

If you don't have the actual definition of the token canister with the canister method implementations, you can always create your own canister definition without method implementations:

    import { Canister, ic, nat64, Principal, update } from 'azle';

    const TokenCanister = Canister({
        transfer: update([Principal, nat64], nat64)
    });

    const tokenCanister = TokenCanister(
        Principal.fromText('r7inp-6aaaa-aaaaa-aaabq-cai')
    );

    export default Canister({
        payout: update([Principal, nat64], nat64, async (to, amount) => {
            return await ic.call(tokenCanister.transfer, {
                args: [to, amount]
            });
        })
    });

The IC guarantees that cross-canister calls will return. This means that, generally speaking, you will always receive a response from `ic.call`. If there are errors during the call, `ic.call` will throw. Wrapping your cross-canister call in a `try...catch` allows you to handle these errors.

Let's add to our example code and explore adding some practical error-handling to stop people from stealing tokens.

`token_canister`:

    import {
        Canister,
        ic,
        nat64,
        Opt,
        Principal,
        StableBTreeMap,
        update
    } from 'azle';

    let accounts = StableBTreeMap<Principal, nat64>(0);

    export default Canister({
        transfer: update([Principal, nat64], nat64, (to, amount) => {
            const from = ic.caller();

            const fromBalance = getBalance(accounts.get(from));

            if (amount > fromBalance) {
                throw new Error(`${from} has an insufficient balance`);
            }

            const toBalance = getBalance(accounts.get(to));

            accounts.insert(from, fromBalance - amount);
            accounts.insert(to, toBalance + amount);

            return amount;
        })
    });

    function getBalance(accountOpt: Opt<nat64>): nat64 {
        if ('None' in accountOpt) {
            return 0n;
        } else {
            return accountOpt.Some;
        }
    }

`payout_canister`:

    import { Canister, ic, nat64, Principal, update } from 'azle';
    import TokenCanister from './index';

    const tokenCanister = TokenCanister(
        Principal.fromText('bkyz2-fmaaa-aaaaa-qaaaq-cai')
    );

    export default Canister({
        payout: update([Principal, nat64], nat64, async (to, amount) => {
            try {
                return await ic.call(tokenCanister.transfer, {
                    args: [to, amount]
                });
            } catch (error) {
                console.log(error);
            }

            return 0n;
        })
    });

Throwing will allow you to express error conditions and halt execution, but you may find embracing the `Result` variant as a better solution for error handling because of its composability and predictability.

So far we have only shown a cross-canister call from an update method. Update methods can call other update methods or query methods (but not composite query methods as discussed below). If an update method calls a query method, that query method will be called in replicated mode. Replicated mode engages the consensus process, but for queries the state will still be discarded.

Cross-canister calls can also be initiated from query methods. These are known as composite queries, and in Azle they are simply `async` query methods. Composite queries can call other composite query methods and regular query methods. Composite queries cannot call update methods.

Here's an example of a composite query method:

    import { bool, Canister, ic, Principal, query } from 'azle';

    const SomeCanister = Canister({
        queryForBoolean: query([], bool)
    });

    const someCanister = SomeCanister(
        Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
    );

    export default Canister({
        querySomeCanister: query([], bool, async () => {
            return await ic.call(someCanister.queryForBoolean);
        })
    });

You can expect cross-canister calls within the same subnet to take up to a few seconds to complete, and cross-canister calls across subnets take about double that time. Composite queries should be much faster, similar to query calls in latency.

If you don't need to wait for your cross-canister call to return, you can use `notify`:

    import { Canister, ic, Principal, update, Void } from 'azle';

    const SomeCanister = Canister({
        receiveNotification: update([], Void)
    });

    const someCanister = SomeCanister(
        Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
    );

    export default Canister({
        sendNotification: update([], Void, () => {
            return ic.notify(someCanister.receiveNotification);
        })
    });

If you need to send cycles with your cross-canister call, you can add `cycles` to the `config` object of `ic.notify`:

    import { Canister, ic, Principal, update, Void } from 'azle';

    const SomeCanister = Canister({
        receiveNotification: update([], Void)
    });

    const someCanister = SomeCanister(
        Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai')
    );

    export default Canister({
        sendNotification: update([], Void, () => {
            return ic.notify(someCanister.receiveNotification, {
                cycles: 1_000_000n
            });
        })
    });