Skip to main content

Rules

Rules are programmable restrictions that you can apply to any action in the TokenPolicy. They are the tool of compliance, regulation, and enforcement of certain business logic in the closed-loop system.

Rule structure

A rule is represented as a witness - a type with a drop ability. You can either encode it in your application logic, or include it as part of a separate module for a more modular approach.

/// The Rule type
struct Rule has drop {}

After you add a rule to an action in the TokenPolicy, the action requires a stamp of the rule to pass confirmation.

See the Approving actions section for more details on how to approve an action.

Modular rules

You can publish rules as separate reusable modules. This enables you to create a library of rules that you can use in different token policies, maximizing code reuse and minimizing the risk of errors.

A rule module is a regular module with a verify-like function that typically takes a TokenPolicy, ActionRequest, and a TxContext as arguments. The function is responsible for verifying the action and stamping the ActionRequest with the rule type.

module example::pass_rule {
use sui::tx_context;
use sui::token::{Self, ActionRequest, TokenPolicy};

/// The Rule type
struct Pass has drop {}

/// Add approval from the Pass rule to the ActionRequest
public fun verify<T>(
_policy: &TokenPolicy<T>,
action_request: &mut ActionRequest<T>,
ctx: &mut TxContext,
) {
// ...
token::add_approval(Pass {}, action_request, ctx)
}
}

Rule configuration

Some rules, such as denylist or allowlist require configuration. For example, a denylist rule might require a list of addresses that are not allowed to perform certain actions. A rule module can define a configuration structure and provide functions to add, modify, retrieve, and remove the configuration.

info

A single rule has a single configuration, even when assigned to multiple actions. If there's a need to have configuration per action, a rule module needs to define a storage structure that can hold and manage multiple configurations.

The configuration system comes with a set of guarantees to protect token owners from malicious actions (or upgrades) from rule module developers:

  1. The rule module defines the type and structure of the configuration.
  2. Addition or modification and removal of the configuration are available only to the TokenPolicy owner.
  3. Only a rule can read the configuration.
  4. Rules cannot modify the configuration without the TokenPolicy owner's approval.

The only attack vector available to the rule creator is upgrading the module and creating a function to bypass the restriction. Make sure to use rules provided by a trusted developer.

Configuration API

The sui::token module defines the configuration API and has the following set of functions.

Add new configuration

A rule must approve new configurations (the rule witness) and the TokenPolicy owner. The type of the configuration can be any as long as it has the store ability.

// module: sui::token
public fun add_rule_config<T, Rule: drop, Config: store>(
_rule: Rule,
policy: &mut TokenPolicy<T>,
policy_cap: &TokenPolicyCap<T>,
config: Config,
_ctx: &mut TxContext
);

Read the configuration

Rules can read the configuration stored in the TokenPolicy.

// module: sui::token
public fun rule_config<T, Rule: drop, Config: store>(
_rule: Rule, policy: &TokenPolicy<T>
): &Config;

Modify the configuration

A rule must approve configuration modifications (the rule witness) as well as the TokenPolicy owner.

// module: sui::token
public fun rule_config_mut<T, Rule: drop, Config: store>(
_rule: Rule, policy: &mut TokenPolicy<T>, policy_cap: &TokenPolicyCap<T>
): &mut Config;

Remove configuration

A good practice for rules is to provide a method to remove the configuration, as a rule can use a custom type for it. However, a token owner can always call the remove_rule_config function to remove the configuration.

// module: sui::token
public fun remove_rule_config<T, Rule, Config: store>(
policy: &mut TokenPolicy<T>,
policy_cap: &TokenPolicyCap<T>,
_ctx: &mut TxContext
): Config;

Because the configuration has store, the token owner can wrap and transfer or store the configuration somewhere else. If the Config type has drop, the value can be ignored.

Cheatsheet: Rule configuration API

Method nameDescriptionNotes
add_rule_configAdds a new config for the ruleRequires rule witness and token owner approval
remove_rule_configRemoves config object from the policyToken Owner can perform any time
rule_configAccess the config immutablyOnly available to a rule
rule_config_mutGet mutable reference to configRequires rule witness and token owner approval
has_rule_configCheck if the rule has a config set-
has_rule_config_with_typeCheck if the rule has a config with type-