What is happening when defining the interface and calling the canister from the principal

Hello!
I am creating a test simple DEX with reference to the defi in the dfinity example, but while reading the code, I had a question on the following part.

[type.mo]

module {
    public type Token = Principal;

    public type DIPInterface = actor {
        transfer : (Principal,Nat) ->  async TxReceipt;
        transferFrom : (Principal,Principal,Nat) -> async TxReceipt;
        allowance : (owner: Principal, spender: Principal) -> async Nat;
        getMetadata: () -> async Metadata;
    };
};

[main.mo]

import T "types";

actor class Dex() = this {
    public shared(msg) func deposit(token: T.Token): async T.DepositReceipt {
        Debug.print("Depositing Token: " # Principal.toText(token) # " LEDGER: " # Principal.toText(ledger));
        if (token == ledger) {
            await depositIcp(msg.caller)
        } else {
            await depositDip(msg.caller, token)
        }
    };

    // After user approves tokens to the DEX
    private func depositDip(caller: Principal, token: T.Token): async T.DepositReceipt {
        // cast token to actor
        let dip20 = actor (Principal.toText(token)) : T.DIPInterface;

        // get DIP fee
        let dip_fee = await fetch_dip_fee(token);

        // Check DIP20 allowance for DEX
        let balance : Nat = (await dip20.allowance(caller, Principal.fromActor(this)));

        // Transfer to account.
        let token_reciept = if (balance > dip_fee) {
            await dip20.transferFrom(caller, Principal.fromActor(this),balance - dip_fee);
        } else {
            return #Err(#BalanceLow);
        };

        switch token_reciept {
            case (#Err e) {
                return #Err(#TransferFailure);
            };
            case _ {};
        };
        let available = balance - dip_fee;

        // add transferred amount to user balance
        book.addTokens(caller,token,available);

        // Return result
        #Ok(available)
    };
};

The example deploys two original tokens using the DIP20 token standard.

I understand that I have two original tokens and I want to define an interface to call the token canister’s method, but why can I just specify the principal and it will be cast to the appropriate token actor?

I would like to know what is actually happening on in the casting (or rather creation of the entity?)
[motoko.mo : depositDip()]

        // cast token to actor
        let dip20 = actor (Principal.toText(token)) : T.DIPInterface;

Does the program call the appropriate token canister actor by reference to the interface and canister ID (principal) defined in Candid?

Thank you in advance for your response.

From what I understand, not much actually happens. The type you specify for the actor (that’s : T.DIPInterface;’ is just a hint for the compiler so that it can structure the calls properly AFAIU. The actor you provide is simply a reference to a canister (Principal), and the type is the proper interface to use for the calls.

The canister comes from the Principal you give the actor (actor (Principal.toText(token))).
The interface is taken from the type you supply it (: T.DIPInterface;). This type can either be defined in Motoko, or by importing a candid file (e.g. by defining it as a remote canister like here).

1 Like