Rules
Rules are programmable restrictions that can be applied 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. It can be either encoded in application logic, or, for more modular setup, can be part of a separate module.
/// The Rule type
struct Rule has drop {}
Once added to an action in the TokenPolicy, the action will require a "stamp" of the Rule to pass confirmation.
See Approving Actions section for more details on how to approve an action.
Modular Rules
Rules can be published as separate reusable modules. This allows to create a library of rules that can be used 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 may 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.
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.
Configuration system comes with a set of guarantees to protect token owners from malicious actions (or upgrades) from Rule module developers:
- The type and the structure of the configuration are defined by the Rule module.
- Addition / modification and removal of the configuration are only available to the
TokenPolicy
owner. - The configuration can be read only by a Rule.
- Rule 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 configuration API is defined in the sui::token
module and has the following set of functions.
Add new Config
New configuration must be approved by a Rule (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 Config
Rule can read 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 Config
The configuration modification must be approved by a Rule (the Rule witness) and 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 a Rule is to provide method to remove the configuration as Rule can use a custom type for it. However, token owner can always remove the configuration by calling the remove_rule_config
function.
// 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 name | Description | Notes |
---|---|---|
add_rule_config | Adds a new Config for the Rule | Requires Rule witness and Token Owner approval |
remove_rule_config | Removes Config object from the Policy | Can be performed any time by the Token Owner |
rule_config | Access the Config immutably | Only available to a Rule |
rule_config_mut | Get mutable reference to Config | Requires Rule witness and Token Owner approval |
has_rule_config | Check if the Rule has a Config set | - |
has_rule_config_with_type | Check if the Rule has a Config with type | - |