demergent-labs / azle

A WebAssembly runtime for TypeScript and JavaScript on ICP
MIT License
199 stars 35 forks source link

Expose installer information on the global ic object #271

Open dansteren opened 2 years ago

dansteren commented 2 years ago

User Story

An a developer writing a TS/JS app with Azle, I want a way to access the ID of the principal that installed the canister from within my canister functions So that I can expose it through my API or base logic off of it.


When implementing #68 I discovered that we weren't exposing this information. That example needs access to the principal that installed the canister but there is likely other information stored as well. We should expose it all.

This should likely be exposed on the global ic object as ic.installer.

dansteren commented 2 years ago

This user story can already be accomplished by manually storing the ic.caller() in a variable from within the Init function like so:

let installer: Principal;

export function init(): Init {
  installer = ic.caller();
}

As I understand it, Motoko merely gives you some syntactic sugar for doing this by way of binding the argument to the this keyword. Alternatively you could manually store the variable like we do in JS, both are functionally equivalent. The former is just a shorthand. To recap, I believe the following to examples are equivalent:

  1. Binding the message to this and making it accessible to all functions in the future:

    shared (message) actor class MyCanister = this {
    public query func installer() : async Principal {
    return message.caller;
    };
    }
  2. Or explicitly storing the message from the initialization in a variable for access later:

    shared (message) actor class MyCanister {
    let owner = message.caller;
    public query func installer() : async Principal {
    return owner.caller;
    };
    }

TLDR; we shouldn't implement this.

See https://smartcontracts.org/docs/current/developer-docs/build/languages/motoko/caller-id/ See https://github.com/demergent-labs/azle/pull/275#issuecomment-1126650319

dansteren commented 2 years ago

To completely backtrack on my last comment, I believe that a case can still be made for exposing the canister installer on the global ic object.

After my last comment I continued working on #68. While running further tests I found that there are some nuances in motoko that surface on re-deploys.

Consider the following motoko canister:

import Principal "mo:base/Principal";

shared (msg) actor class MyCanister = this {
  public query func installer() : async Principal {
    return msg.caller;
  };
};

After running dfx deploy a single time, calling dfx canister call my_canister installer would return the principal that installed the canister.

Now suppose you:

  1. create a new identity
  2. make it a controller of the canister
  3. add another method (so that the hash changes and you can redeploy)
  4. redeploy the canister from the new identity
  5. run dfx canister call my_canister installer

At this point the new principal would be returned, not the principal from the initial code install. In effect then, what is happening is that motoko creates a post_deploy hook and overwrites the msg variable with the new message from the re-deploy. To get this same functionality in azle, you have to make sure to create your own post-deploy hook and manually save that variable.

Since the only attribute that msg has is the caller, this seems to contradict the precedence set by ic.caller which is updated automatically by azle.

I therefore think that we should similarly save the installer of the canister for the user and expose it as ic.installer since that removes the need for each developer to define a post-upgrade hook to get this behavior, which also brings us more in line with the behavior motoko gives out of the box.