Creating Managed Access

This example demonstrates the use of the FlatAdmin blueprint to manage access in another blueprint.

You can find the code for the FlatAdmin blueprint here and the code for the ManagedAccess blueprint here.

Note that in order for this example to function, you will have to publish the package containing the FlatAdmin blueprint to your simulator to a specific address (or change the address imported near the top of lib.rs in this package).

If you wish to publish FlatAdmin to the appropriate address, switch to that directory and run:

resim publish . --package-address package_sim1q9h0dr0z36zaq6h66lg5putxtztyf0sgelxu654r67ks765aue

Importing & Calling a Blueprint

Currently, importing another blueprint requires a few manual steps. We expect to simplify this process in the future, but for now here are the steps:

  1. Publish the package containing the blueprint you wish to import.

  2. Import it using the external_blueprint! macro. Example:

use scrypto::prelude::*;

external_blueprint! {
  {
      package: "package_sim1q9h0dr0z36zaq6h66lg5putxtztyf0sgelxu654r67ks765aue",
      blueprint: "FlatAdmin"
  },
  FlatAdmin {
    fn instantiate_flat_admin(badge_name: String) -> (ComponentAddress, Bucket);
    fn create_additional_admin(&mut self) -> Bucket;
    fn destroy_admin_badge(&mut self, to_destroy: Bucket);
    fn get_admin_badge_address(&self) -> ResourceAddress;
  }
}

Now you’ll be able to call functions on that blueprint like so: FlatAdmin::some_function(<args>)

Resources and Data

struct ManagedAccess {
  admin_badge: ResourceAddress,
  flat_admin_controller: ComponentAddress,
  protected_vault: Vault,
}

Our instantiated component will maintain a single vault which stores XRD. Anyone may deposit to the vault, but only a caller in possession of an admin badge may withdraw from it.

The only state we need to maintain is the aforementioned vault, and the ResourceAddress of the badge used for authorization. As a convenience for the user, we will also store the address of the FlatAdmin component which manages the supply of those badges.

Getting Ready for Instantiation

In order to instantiate the ManagedAccess component, we’ll require no parameters and return to the caller a tuple containing the newly instantiated component address, and a bucket containing the first admin badge created by our FlatAdmin badge manager:

pub fn instantiate_managed_access() -> (ComponentAddress, Bucket) {

Our first step will be to instantiate a FlatAdmin component, and store the results of that instantiation.

let (flat_admin_component, admin_badge) =
  FlatAdmin::instantiate_flat_admin("My Managed Access Badge".into());

We then need to specify that only a holder of the admin badge may withdraw funds from a managed access component.

let rules = AccessRules::new()
  .method("withdraw_all", rule!(require(admin_badge.resource_address())))
  .default(rule!(allow_all));

That gives us everything we need to populate our component state, instantiate it, and return the results to our caller:

let mut component = Self {
    admin_badge: admin_badge.resource_address(),
    flat_admin_controller: flat_admin_component,
    protected_vault: Vault::new(RADIX_TOKEN),
}
.instantiate();
component.add_access_check(rules);
let component = component.globalize();

(component, admin_badge)

Adding Methods

First, we’ll create a protected method to allow withdrawal. This method is protected through the v0.4.0 system-level authentication system. Meaning, the system will automatically check that an admin badge is present in the Auth Zone before allowing the call to take place.

pub fn withdraw_all(&mut self) -> Bucket {
  self.protected_vault.take_all()
}

The rest of the methods are straightforward. We’ll add a method to permit anyone to deposit XRD, and then some read-only methods to return data about our admin badge and the FlatAdmin controller which manages the supply of badges.

pub fn deposit(&mut self, to_deposit: Bucket) {
  self.protected_vault.put(to_deposit);
}

pub fn get_admin_badge_address(&self) -> ResourceAddress {
    self.admin_badge
}

pub fn get_flat_admin_controller_address(&self) -> ComponentAddress {
    self.flat_admin_controller
}

That’s it. Access control components like FlatAdmin are expected to be very commonly consumed by other blueprints, as they provide consistent, re-usable mechanisms to manage privileges.

Trying this example with resim

Let’s deploy, instantiate, and call methods on our component

  1. Create a new account, and save the account address:

    resim new-account
  2. Publish the FlatAdmin and ManagedAccess packages:

    cd core/flat-admin/
    resim publish . --package-address package_sim1q9h0dr0z36zaq6h66lg5putxtztyf0sgelxu654r67ks765aue
    cd ../managed-access/
    resim publish .
  3. Call the instantiate_managed_access function to instantiate a ManagedAccess component. Save the second returned component address and second resource address (which is the admin badge):

    resim call-function <MANAGED_ACCESS_PACKAGE_ADDRESS> ManagedAccess instantiate_managed_access
  4. Call the deposit method of the component we just instantiated:

    resim call-method <COMPONENT_ADDRESS> deposit 1000,resource_sim1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqu57yag
  5. Look at the resources in your account to get the address of the admin badge:

    resim show <ACCOUNT_ADDRESS>
  6. Call the withdraw_all method by running this command:

    resim call-method <COMPONENT_ADDRESS> withdraw_all --proofs 1,<ADMIN_BADGE_ADDRESS>