From cc397cb8df5570ce3ca91027141112c9c730f6ba Mon Sep 17 00:00:00 2001 From: thounyy Date: Fri, 11 Oct 2024 03:37:02 +0200 Subject: [PATCH] feat: add access_control --- packages/actions/sources/access_control.move | 153 +++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 packages/actions/sources/access_control.move diff --git a/packages/actions/sources/access_control.move b/packages/actions/sources/access_control.move new file mode 100644 index 0000000..c327b42 --- /dev/null +++ b/packages/actions/sources/access_control.move @@ -0,0 +1,153 @@ +/// Developers can restrict access to functions in their own package with an Access. +/// Access structs are issued using a Cap type that can be locked at any time. +/// +/// The Access has copy and drop abilities to be used multiple times within a single PTB +/// It has a generic type which acts as a proof of cap. +/// It is similar to the Cap pattern but it is issued by an Account upon proposal execution. +/// +/// e.g. +/// +/// public struct AdminCap has key, store {} +/// +/// public fun foo(_: Access) { ... } + +module account_actions::access_control; + +// === Imports === + +use std::{ + type_name, + string::String +}; +use account_protocol::{ + account::Account, + proposals::{Proposal, Expired}, + executable::Executable, + auth::Auth +}; + +// === Errors === + +const ENoLock: u64 = 0; + +// === Structs === + +/// Dynamic Field key for the AccessLock +public struct AccessKey has copy, drop, store {} +/// Dynamic Field wrapper storing a cap +public struct AccessLock has store { + cap: Cap, +} + +/// [MEMBER] can lock a custom Cap in the Account to restrict access to certain functions +public struct Do() has drop; +/// [PROPOSAL] issues an Access, Cap being the type of a Cap held by the Account +public struct AccessProposal() has drop; + +/// [ACTION] mint new coins +public struct AccessAction has store {} + +/// This struct is created upon approval to grant access to certain functions gated by an Access type +/// Similar to a cap but issued by an Account, with copy and drop to be used multiple times within a single PTB +public struct Access has copy, drop {} + +// === [MEMBER] Public functions === + +/// Only a member can lock a Cap, the Cap must have at least store ability +public fun lock_cap( + auth: Auth, + account: &mut Account, + cap: Cap, +) { + auth.verify(account.addr()); + + let lock = AccessLock { cap }; + account.add_managed_asset(Do(), AccessKey {}, lock); +} + +public fun has_lock( + account: &Account +): bool { + account.has_managed_asset(AccessKey {}) +} + +// === [PROPOSAL] Public functions === + +// step 1: propose to mint an amount of a coin that will be transferred to the Account +public fun propose_access( + auth: Auth, + account: &mut Account, + outcome: Outcome, + key: String, + description: String, + execution_time: u64, + expiration_epoch: u64, + ctx: &mut TxContext +) { + assert!(has_lock(account), ENoLock); + + let mut proposal = account.create_proposal( + auth, + outcome, + AccessProposal(), + type_to_name(), // the coin type is the auth name + key, + description, + execution_time, + expiration_epoch, + ctx + ); + + new_access(&mut proposal, AccessProposal()); + account.add_proposal(proposal, AccessProposal()); +} + +// step 2: multiple members have to approve the proposal (account::approve_proposal) +// step 3: execute the proposal and return the action (AccountConfig::module::execute_proposal) + +// step 4: mint the coins and send them to the account +public fun execute_access( + mut executable: Executable, + account: &mut Account, +): Access { + let access = access(&mut executable, account, AccessProposal()); + + destroy_access(&mut executable, AccessProposal()); + executable.destroy(AccessProposal()); + + access +} + +// === [ACTION] Public functions === + +public fun new_access( + proposal: &mut Proposal, + witness: W, +) { + proposal.add_action(AccessAction {}, witness); +} + +public fun access( + executable: &mut Executable, + account: &mut Account, + witness: W, +): Access { + let _access_mut: &mut AccessAction = executable.action_mut(account.addr(), witness); + assert!(has_lock(account), ENoLock); + + Access {} +} + +public fun destroy_access(executable: &mut Executable, witness: W) { + let AccessAction {} = executable.remove_action(witness); +} + +public fun delete_access_action(expired: Expired) { + let AccessAction { .. } = expired.remove_expired_action(); +} + +// === Private functions === + +fun type_to_name(): String { + type_name::get().into_string().to_string() +}