sui::deny_list
Defines the DenyList
type. The DenyList
shared object is used to restrict access to
instances of certain core types from being used as inputs by specified addresses in the deny
list.
DenyList
ConfigWriteCap
ConfigKey
AddressKey
GlobalPauseKey
PerTypeConfigCreated
PerTypeList
v2_add
v2_remove
v2_contains_current_epoch
v2_contains_next_epoch
v2_enable_global_pause
v2_disable_global_pause
v2_is_global_pause_enabled_current_epoch
v2_is_global_pause_enabled_next_epoch
migrate_v1_to_v2
add_per_type_config
borrow_per_type_config_mut
borrow_per_type_config
per_type_exists
per_type_config_entry
v1_add
v1_per_type_list_add
v1_remove
v1_per_type_list_remove
v1_contains
v1_per_type_list_contains
create
per_type_list
use std::ascii;
use std::bcs;
use std::option;
use std::string;
use std::vector;
use sui::address;
use sui::bag;
use sui::config;
use sui::dynamic_field;
use sui::dynamic_object_field;
use sui::event;
use sui::hex;
use sui::object;
use sui::party;
use sui::table;
use sui::transfer;
use sui::tx_context;
use sui::vec_map;
use sui::vec_set;
DenyList
A shared object that stores the addresses that are blocked for a given core type.
public struct DenyList has key
id: sui::object::UID
lists: sui::bag::Bag
ConfigWriteCap
The capability used to write to the deny list config. Ensures that the Configs for the DenyList are modified only by this module.
public struct ConfigWriteCap has drop
ConfigKey
The dynamic object field key used to store the Config
for a given type, essentially a
(per_type_index, per_type_key)
pair.
public struct ConfigKey has copy, drop, store
per_type_index: u64
per_type_key: vector<u8>
AddressKey
The setting key used to store the deny list for a given address in the Config
.
public struct AddressKey has copy, drop, store
0: address
GlobalPauseKey
The setting key used to store the global pause setting in the Config
.
public struct GlobalPauseKey has copy, drop, store
PerTypeConfigCreated
The event emitted when a new Config
is created for a given type. This can be useful for
tracking the ID
of a type’s Config
object.
public struct PerTypeConfigCreated has copy, drop, store
key: sui::deny_list::ConfigKey
config_id: sui::object::ID
PerTypeList
Stores the addresses that are denied for a given core type.
public struct PerTypeList has key, store
id: sui::object::UID
denied_count: sui::table::Table<address, u64>
denied_addresses: sui::table::Table<vector<u8>, sui::vec_set::VecSet<address>>
sui::coin::Coin
: If addresses A and B are banned from using
"0...0123::my_coin::MY_COIN", this will be "0...0123::my_coin::MY_COIN" -> {A, B}.
Trying to create a deny list object when not called by the system address.
const ENotSystemAddress: u64 = 0;
The specified address to be removed is not already in the deny list.
const ENotDenied: u64 = 1;
The specified address cannot be added to the deny list.
const EInvalidAddress: u64 = 1;
The index into the deny list vector for the sui::coin::Coin
type.
const COIN_INDEX: u64 = 0;
These addresses are reserved and cannot be added to the deny list. The addresses listed are well known package and object addresses. So it would be meaningless to add them to the deny list.
const RESERVED: vector<address> = vector[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x403, 0xdee9];
v2_add
public(package) fun v2_add(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, addr: address, ctx: &mut sui::tx_context::TxContext)
public(package) fun v2_add(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
addr: address,
ctx: &mut TxContext,
) {
let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx);
let setting_name = AddressKey(addr);
let next_epoch_entry = per_type_config.entry!<_, AddressKey, bool>(
&mut ConfigWriteCap(),
setting_name,
|_deny_list, _cap, _ctx| true,
ctx,
);
*next_epoch_entry = true;
}
v2_remove
public(package) fun v2_remove(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, addr: address, ctx: &mut sui::tx_context::TxContext)
public(package) fun v2_remove(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
addr: address,
ctx: &mut TxContext,
) {
let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx);
let setting_name = AddressKey(addr);
per_type_config.remove_for_next_epoch<_, AddressKey, bool>(
&mut ConfigWriteCap(),
setting_name,
ctx,
);
}
v2_contains_current_epoch
public(package) fun v2_contains_current_epoch(deny_list: &sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, addr: address, ctx: &sui::tx_context::TxContext): bool
public(package) fun v2_contains_current_epoch(
deny_list: &DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
addr: address,
ctx: &TxContext,
): bool {
if (!deny_list.per_type_exists(per_type_index, per_type_key)) return false;
let per_type_config = deny_list.borrow_per_type_config(per_type_index, per_type_key);
let setting_name = AddressKey(addr);
config::read_setting(object::id(per_type_config), setting_name, ctx).destroy_or!(false)
}
v2_contains_next_epoch
public(package) fun v2_contains_next_epoch(deny_list: &sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, addr: address): bool
public(package) fun v2_contains_next_epoch(
deny_list: &DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
addr: address,
): bool {
if (!deny_list.per_type_exists(per_type_index, per_type_key)) return false;
let per_type_config = deny_list.borrow_per_type_config(per_type_index, per_type_key);
let setting_name = AddressKey(addr);
per_type_config.read_setting_for_next_epoch(setting_name).destroy_or!(false)
}
v2_enable_global_pause
public(package) fun v2_enable_global_pause(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, ctx: &mut sui::tx_context::TxContext)
public(package) fun v2_enable_global_pause(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
ctx: &mut TxContext,
) {
let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx);
let setting_name = GlobalPauseKey();
let next_epoch_entry = per_type_config.entry!<_, GlobalPauseKey, bool>(
&mut ConfigWriteCap(),
setting_name,
|_deny_list, _cap, _ctx| true,
ctx,
);
*next_epoch_entry = true;
}
v2_disable_global_pause
public(package) fun v2_disable_global_pause(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, ctx: &mut sui::tx_context::TxContext)
public(package) fun v2_disable_global_pause(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
ctx: &mut TxContext,
) {
let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx);
let setting_name = GlobalPauseKey();
per_type_config.remove_for_next_epoch<_, GlobalPauseKey, bool>(
&mut ConfigWriteCap(),
setting_name,
ctx,
);
}
v2_is_global_pause_enabled_current_epoch
public(package) fun v2_is_global_pause_enabled_current_epoch(deny_list: &sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, ctx: &sui::tx_context::TxContext): bool
public(package) fun v2_is_global_pause_enabled_current_epoch(
deny_list: &DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
ctx: &TxContext,
): bool {
if (!deny_list.per_type_exists(per_type_index, per_type_key)) return false;
let per_type_config = deny_list.borrow_per_type_config(per_type_index, per_type_key);
let setting_name = GlobalPauseKey();
config::read_setting(object::id(per_type_config), setting_name, ctx).destroy_or!(false)
}
v2_is_global_pause_enabled_next_epoch
public(package) fun v2_is_global_pause_enabled_next_epoch(deny_list: &sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>): bool
public(package) fun v2_is_global_pause_enabled_next_epoch(
deny_list: &DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
): bool {
if (!deny_list.per_type_exists(per_type_index, per_type_key)) return false;
let per_type_config = deny_list.borrow_per_type_config(per_type_index, per_type_key);
let setting_name = GlobalPauseKey();
per_type_config.read_setting_for_next_epoch(setting_name).destroy_or!(false)
}
migrate_v1_to_v2
public(package) fun migrate_v1_to_v2(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, ctx: &mut sui::tx_context::TxContext)
public(package) fun migrate_v1_to_v2(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
ctx: &mut TxContext,
) {
let bag_entry: &mut PerTypeList = &mut deny_list.lists[per_type_index];
let elements = if (!bag_entry.denied_addresses.contains(per_type_key)) vector[] else bag_entry
.denied_addresses
.remove(per_type_key)
.into_keys();
elements.do_ref!(|addr| {
let addr = *addr;
let denied_count = &mut bag_entry.denied_count[addr];
*denied_count = *denied_count - 1;
if (*denied_count == 0) {
bag_entry.denied_count.remove(addr);
}
});
let per_type_config = deny_list.per_type_config_entry!(per_type_index, per_type_key, ctx);
elements.do!(|addr| {
let setting_name = AddressKey(addr);
let next_epoch_entry = per_type_config.entry!<_, AddressKey, bool>(
&mut ConfigWriteCap(),
setting_name,
|_deny_list, _cap, _ctx| true,
ctx,
);
*next_epoch_entry = true;
});
}
add_per_type_config
fun add_per_type_config(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>, ctx: &mut sui::tx_context::TxContext)
fun add_per_type_config(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
ctx: &mut TxContext,
) {
let key = ConfigKey { per_type_index, per_type_key };
let config = config::new(&mut ConfigWriteCap(), ctx);
let config_id = object::id(&config);
ofield::internal_add(&mut deny_list.id, key, config);
sui::event::emit(PerTypeConfigCreated { key, config_id });
}
borrow_per_type_config_mut
fun borrow_per_type_config_mut(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>): &mut sui::config::Config<sui::deny_list::ConfigWriteCap>
fun borrow_per_type_config_mut(
deny_list: &mut DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
): &mut Config<ConfigWriteCap> {
let key = ConfigKey { per_type_index, per_type_key };
ofield::internal_borrow_mut(&mut deny_list.id, key)
}
borrow_per_type_config
fun borrow_per_type_config(deny_list: &sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>): &sui::config::Config<sui::deny_list::ConfigWriteCap>
fun borrow_per_type_config(
deny_list: &DenyList,
per_type_index: u64,
per_type_key: vector<u8>,
): &Config<ConfigWriteCap> {
let key = ConfigKey { per_type_index, per_type_key };
ofield::internal_borrow(&deny_list.id, key)
}
per_type_exists
fun per_type_exists(deny_list: &sui::deny_list::DenyList, per_type_index: u64, per_type_key: vector<u8>): bool
fun per_type_exists(deny_list: &DenyList, per_type_index: u64, per_type_key: vector<u8>): bool {
let key = ConfigKey { per_type_index, per_type_key };
ofield::exists_(&deny_list.id, key)
}
per_type_config_entry
macro fun per_type_config_entry($deny_list: &mut sui::deny_list::DenyList, $per_type_index: u64, $per_type_key: vector<u8>, $ctx: &mut sui::tx_context::TxContext): &mut sui::config::Config<sui::deny_list::ConfigWriteCap>
macro fun per_type_config_entry(
$deny_list: &mut DenyList,
$per_type_index: u64,
$per_type_key: vector<u8>,
$ctx: &mut TxContext,
): &mut Config<ConfigWriteCap> {
let deny_list = $deny_list;
let per_type_index = $per_type_index;
let per_type_key = $per_type_key;
let ctx = $ctx;
if (!deny_list.per_type_exists(per_type_index, per_type_key)) {
deny_list.add_per_type_config(per_type_index, per_type_key, ctx);
};
deny_list.borrow_per_type_config_mut(per_type_index, per_type_key)
}
v1_add
Adds the given address to the deny list of the specified type, preventing it from interacting with instances of that type as an input to a transaction. For coins, the type specified is the type of the coin, not the coin type itself. For example, “00…0123::my_coin::MY_COIN” would be the type, not “00…02::coin::Coin”.
public(package) fun v1_add(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, type: vector<u8>, addr: address)
public(package) fun v1_add(
deny_list: &mut DenyList,
per_type_index: u64,
`type`: vector<u8>,
addr: address,
) {
let reserved = RESERVED;
assert!(!reserved.contains(&addr), EInvalidAddress);
let bag_entry: &mut PerTypeList = &mut deny_list.lists[per_type_index];
bag_entry.v1_per_type_list_add(`type`, addr)
}
v1_per_type_list_add
fun v1_per_type_list_add(list: &mut sui::deny_list::PerTypeList, type: vector<u8>, addr: address)
fun v1_per_type_list_add(list: &mut PerTypeList, `type`: vector<u8>, addr: address) {
if (!list.denied_addresses.contains(`type`)) {
list.denied_addresses.add(`type`, vec_set::empty());
};
let denied_addresses = &mut list.denied_addresses[`type`];
let already_denied = denied_addresses.contains(&addr);
if (already_denied) return;
denied_addresses.insert(addr);
if (!list.denied_count.contains(addr)) {
list.denied_count.add(addr, 0);
};
let denied_count = &mut list.denied_count[addr];
*denied_count = *denied_count + 1;
}
v1_remove
Removes a previously denied address from the list.
Aborts with ENotDenied
if the address is not on the list.
public(package) fun v1_remove(deny_list: &mut sui::deny_list::DenyList, per_type_index: u64, type: vector<u8>, addr: address)
public(package) fun v1_remove(
deny_list: &mut DenyList,
per_type_index: u64,
`type`: vector<u8>,
addr: address,
) {
let reserved = RESERVED;
assert!(!reserved.contains(&addr), EInvalidAddress);
let bag_entry: &mut PerTypeList = &mut deny_list.lists[per_type_index];
bag_entry.v1_per_type_list_remove(`type`, addr)
}
v1_per_type_list_remove
fun v1_per_type_list_remove(list: &mut sui::deny_list::PerTypeList, type: vector<u8>, addr: address)
fun v1_per_type_list_remove(list: &mut PerTypeList, `type`: vector<u8>, addr: address) {
let denied_addresses = &mut list.denied_addresses[`type`];
assert!(denied_addresses.contains(&addr), ENotDenied);
denied_addresses.remove(&addr);
let denied_count = &mut list.denied_count[addr];
*denied_count = *denied_count - 1;
if (*denied_count == 0) {
list.denied_count.remove(addr);
}
}
v1_contains
Returns true iff the given address is denied for the given type.
public(package) fun v1_contains(deny_list: &sui::deny_list::DenyList, per_type_index: u64, type: vector<u8>, addr: address): bool
public(package) fun v1_contains(
deny_list: &DenyList,
per_type_index: u64,
`type`: vector<u8>,
addr: address,
): bool {
let reserved = RESERVED;
if (reserved.contains(&addr)) return false;
let bag_entry: &PerTypeList = &deny_list.lists[per_type_index];
bag_entry.v1_per_type_list_contains(`type`, addr)
}
v1_per_type_list_contains
fun v1_per_type_list_contains(list: &sui::deny_list::PerTypeList, type: vector<u8>, addr: address): bool
fun v1_per_type_list_contains(list: &PerTypeList, `type`: vector<u8>, addr: address): bool {
if (!list.denied_count.contains(addr)) return false;
let denied_count = &list.denied_count[addr];
if (*denied_count == 0) return false;
if (!list.denied_addresses.contains(`type`)) return false;
let denied_addresses = &list.denied_addresses[`type`];
denied_addresses.contains(&addr)
}
create
Creation of the deny list object is restricted to the system address via a system transaction.
fun create(ctx: &mut sui::tx_context::TxContext)
fun create(ctx: &mut TxContext) {
assert!(ctx.sender() == @0x0, ENotSystemAddress);
let mut lists = bag::new(ctx);
lists.add(COIN_INDEX, per_type_list(ctx));
let deny_list_object = DenyList {
id: object::sui_deny_list_object_id(),
lists,
};
transfer::share_object(deny_list_object);
}
per_type_list
fun per_type_list(ctx: &mut sui::tx_context::TxContext): sui::deny_list::PerTypeList
fun per_type_list(ctx: &mut TxContext): PerTypeList {
PerTypeList {
id: object::new(ctx),
denied_count: table::new(ctx),
denied_addresses: table::new(ctx),
}
}