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 . --address 01a99c5f6d0f4b92e81968405bde0e14709ab6630dc0e215a38eef

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. Export the ABI for that blueprint using the command resim export-abi <package_address> <blueprint_name>

  3. Copy the output of that command, and paste it into the source file you wish to consume it from. Enclose the content within an opening import! { r#" and enclosing "#} block. Example:

use scrypto::prelude::*;

import! {
r#"
<EXPORTED_ABI>
"#
}

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 ResourceDef 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 auth = 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 component = Self {
  admin_badge: admin_badge.resource_address(),
  flat_admin_controller: flat_admin_component,
  protected_vault: Vault::new(RADIX_TOKEN),
}
.instantiate()
.add_access_check(auth)
.globalize();

// Return the instantiated component and admin badge to the caller
(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) -> Address {
  self.admin_badge.address()
}

pub fn get_flat_admin_controller_address(&self) -> Address {
  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 . --address 01a99c5f6d0f4b92e81968405bde0e14709ab6630dc0e215a38eef
    cd ../managed-access/
    resim publish .
  3. Call the instantiate_managed_access function to instantiate a ManagedAccess component, and save the component address:

    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,030000000000000000000000000000000000000000000000000004
  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 specifying the admin badge:

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