The module provides a general implmentation of configuration for onchain contracts.
- Resource
Config
- Struct
ModifyConfigCapability
- Resource
ModifyConfigCapabilityHolder
- Struct
ConfigChangeEvent
- Constants
- Function
get_by_address
- Function
config_exist_by_address
- Function
set
- Function
set_with_capability
- Function
publish_new_config_with_capability
- Function
publish_new_config
- Function
extract_modify_config_capability
- Function
restore_modify_config_capability
- Function
destroy_modify_config_capability
- Function
account_address
- Function
emit_config_change_event
- Module Specification
use 0x1::Errors;
use 0x1::Event;
use 0x1::Option;
use 0x1::Signer;
A generic singleton resource that holds a value of a specific type.
struct Config<ConfigValue: copy, drop, store> has key
Fields
-
payload: ConfigValue
Accounts with this privilege can modify config of type ConfigValue under account_address
struct ModifyConfigCapability<ConfigValue: copy, drop, store> has store
Fields
-
account_address: address
-
events: Event::EventHandle<Config::ConfigChangeEvent<ConfigValue>>
A holder for ModifyConfigCapability, for extraction and restoration of ModifyConfigCapability.
struct ModifyConfigCapabilityHolder<ConfigValue: copy, drop, store> has store, key
Fields
-
cap: Option::Option<Config::ModifyConfigCapability<ConfigValue>>
Event emitted when config value is changed.
struct ConfigChangeEvent<ConfigValue: copy, drop, store> has drop, store
Fields
-
account_address: address
-
value: ConfigValue
const ECAPABILITY_HOLDER_NOT_EXISTS: u64 = 101;
const ECONFIG_VALUE_DOES_NOT_EXIST: u64 = 13;
Get a copy of ConfigValue
value stored under addr
.
public fun get_by_address<ConfigValue: copy, drop, store>(addr: address): ConfigValue
Implementation
public fun get_by_address<ConfigValue: copy + drop + store>(addr: address): ConfigValue acquires Config {
assert!(exists<Config<ConfigValue>>(addr), Errors::invalid_state(ECONFIG_VALUE_DOES_NOT_EXIST));
*&borrow_global<Config<ConfigValue>>(addr).payload
}
Specification
Check whether the config of ConfigValue
type exists under addr
.
public fun config_exist_by_address<ConfigValue: copy, drop, store>(addr: address): bool
Implementation
public fun config_exist_by_address<ConfigValue: copy + drop + store>(addr: address): bool {
exists<Config<ConfigValue>>(addr)
}
Specification
aborts_if false;
ensures result == exists<Config<ConfigValue>>(addr);
Set a config item to a new value with capability stored under signer
public fun set<ConfigValue: copy, drop, store>(account: &signer, payload: ConfigValue)
Implementation
public fun set<ConfigValue: copy + drop + store>(
account: &signer,
payload: ConfigValue,
) acquires Config, ModifyConfigCapabilityHolder {
let signer_address = Signer::address_of(account);
assert!(
exists<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address),
Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS),
);
let cap_holder = borrow_global_mut<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address);
assert!(Option::is_some(&cap_holder.cap), Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS));
set_with_capability(Option::borrow_mut(&mut cap_holder.cap), payload);
}
Specification
let addr = Signer::address_of(account);
let cap_opt = spec_cap<ConfigValue>(addr);
let cap = Option::borrow(spec_cap<ConfigValue>(Signer::address_of(account)));
aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(addr);
aborts_if Option::is_none<ModifyConfigCapability<ConfigValue>>(cap_opt);
ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(addr);
pragma aborts_if_is_partial;
ensures exists<Config<ConfigValue>>(
Option::borrow(spec_cap<ConfigValue>(Signer::address_of(account))).account_address,
);
ensures global<Config<ConfigValue>>(
Option::borrow(spec_cap<ConfigValue>(Signer::address_of(account))).account_address,
).payload == payload;
fun spec_cap<ConfigValue>(addr: address): Option<ModifyConfigCapability<ConfigValue>> {
global<ModifyConfigCapabilityHolder<ConfigValue>>(addr).cap
}
Set a config item to a new value with cap.
public fun set_with_capability<ConfigValue: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<ConfigValue>, payload: ConfigValue)
Implementation
public fun set_with_capability<ConfigValue: copy + drop + store>(
cap: &mut ModifyConfigCapability<ConfigValue>,
payload: ConfigValue,
) acquires Config {
let addr = cap.account_address;
assert!(exists<Config<ConfigValue>>(addr), Errors::invalid_state(ECONFIG_VALUE_DOES_NOT_EXIST));
let config = borrow_global_mut<Config<ConfigValue>>(addr);
config.payload = copy payload;
emit_config_change_event(cap, payload);
}
Specification
Publish a new config item. The caller will use the returned ModifyConfigCapability to specify the access control policy for who can modify the config.
public fun publish_new_config_with_capability<ConfigValue: copy, drop, store>(account: &signer, payload: ConfigValue): Config::ModifyConfigCapability<ConfigValue>
Implementation
public fun publish_new_config_with_capability<ConfigValue: copy + drop + store>(
account: &signer,
payload: ConfigValue,
): ModifyConfigCapability<ConfigValue> acquires ModifyConfigCapabilityHolder{
publish_new_config<ConfigValue>(account, payload);
extract_modify_config_capability<ConfigValue>(account)
}
Specification
include PublishNewConfigAbortsIf<ConfigValue>;
ensures exists<Config<ConfigValue>>(Signer::address_of(account));
ensures global<Config<ConfigValue>>(Signer::address_of(account)).payload == payload;
ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
ensures Option::is_none(global<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account)).cap);
Publish a new config item under account address.
public fun publish_new_config<ConfigValue: copy, drop, store>(account: &signer, payload: ConfigValue)
Implementation
public fun publish_new_config<ConfigValue: copy + drop + store>(account: &signer, payload: ConfigValue) {
move_to(account, Config<ConfigValue>{ payload });
let cap = ModifyConfigCapability<ConfigValue> {
account_address: Signer::address_of(account),
events: Event::new_event_handle<ConfigChangeEvent<ConfigValue>>(account),
};
move_to(account, ModifyConfigCapabilityHolder{cap: Option::some(cap)});
}
Specification
include PublishNewConfigAbortsIf<ConfigValue>;
ensures exists<Config<ConfigValue>>(Signer::address_of(account));
ensures global<Config<ConfigValue>>(Signer::address_of(account)).payload == payload;
ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
ensures Option::is_some(global<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account)).cap);
schema PublishNewConfigAbortsIf<ConfigValue> {
account: signer;
aborts_if exists<Config<ConfigValue>>(Signer::address_of(account));
aborts_if exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
}
schema AbortsIfConfigNotExist<ConfigValue> {
addr: address;
aborts_if !exists<Config<ConfigValue>>(addr);
}
schema AbortsIfConfigOrCapabilityNotExist<ConfigValue> {
addr: address;
aborts_if !exists<Config<ConfigValue>>(addr);
aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(addr);
}
schema PublishNewConfigEnsures<ConfigValue> {
account: signer;
ensures exists<Config<ConfigValue>>(Signer::address_of(account));
ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
}
schema AbortsIfCapNotExist<ConfigValue> {
address: address;
aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(address);
aborts_if Option::is_none<ModifyConfigCapability<ConfigValue>>(
global<ModifyConfigCapabilityHolder<ConfigValue>>(address).cap,
);
}
Extract account's ModifyConfigCapability for ConfigValue type
public fun extract_modify_config_capability<ConfigValue: copy, drop, store>(account: &signer): Config::ModifyConfigCapability<ConfigValue>
Implementation
public fun extract_modify_config_capability<ConfigValue: copy + drop + store>(
account: &signer,
): ModifyConfigCapability<ConfigValue> acquires ModifyConfigCapabilityHolder {
let signer_address = Signer::address_of(account);
assert!(
exists<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address),
Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS)
);
let cap_holder = borrow_global_mut<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address);
Option::extract(&mut cap_holder.cap)
}
Specification
let address = Signer::address_of(account);
include AbortsIfCapNotExist<ConfigValue>;
ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(address);
ensures Option::is_none<ModifyConfigCapability<ConfigValue>>(
global<ModifyConfigCapabilityHolder<ConfigValue>>(address).cap
);
ensures result == old(Option::borrow(global<ModifyConfigCapabilityHolder<ConfigValue>>(address).cap));
Restore account's ModifyConfigCapability
public fun restore_modify_config_capability<ConfigValue: copy, drop, store>(cap: Config::ModifyConfigCapability<ConfigValue>)
Implementation
public fun restore_modify_config_capability<ConfigValue: copy + drop + store>(
cap: ModifyConfigCapability<ConfigValue>,
) acquires ModifyConfigCapabilityHolder {
let cap_holder = borrow_global_mut<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address);
Option::fill(&mut cap_holder.cap, cap);
}
Specification
aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address);
aborts_if Option::is_some(global<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address).cap);
ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address);
ensures Option::is_some(global<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address).cap);
ensures Option::borrow(global<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address).cap) == cap;
Destroy the given ModifyConfigCapability
public fun destroy_modify_config_capability<ConfigValue: copy, drop, store>(cap: Config::ModifyConfigCapability<ConfigValue>)
Implementation
public fun destroy_modify_config_capability<ConfigValue: copy + drop + store>(
cap: ModifyConfigCapability<ConfigValue>,
) {
let ModifyConfigCapability{account_address:_, events} = cap;
Event::destroy_handle(events)
}
Specification
aborts_if false;
Return the address of the given ModifyConfigCapability
public fun account_address<ConfigValue: copy, drop, store>(cap: &Config::ModifyConfigCapability<ConfigValue>): address
Implementation
public fun account_address<ConfigValue: copy + drop + store>(cap: &ModifyConfigCapability<ConfigValue>): address {
cap.account_address
}
Specification
aborts_if false;
ensures result == cap.account_address;
Emit a config change event.
fun emit_config_change_event<ConfigValue: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<ConfigValue>, value: ConfigValue)
Implementation
fun emit_config_change_event<ConfigValue: copy + drop + store>(
cap: &mut ModifyConfigCapability<ConfigValue>,
value: ConfigValue,
) {
Event::emit_event<ConfigChangeEvent<ConfigValue>>(
&mut cap.events,
ConfigChangeEvent {
account_address: cap.account_address,
value,
},
);
}
Specification
aborts_if false;
pragma verify;
pragma aborts_if_is_strict;