SAC Admin Generic
概述
Stellar Asset Contract (SAC) Admin Generic 模块提供了一种使用通用方法为 Stellar Asset Contracts (SAC) 实现自定义管理功能的方式。这种方法利用 __check_auth
函数来处理身份验证和授权逻辑,同时为面向用户和管理功能保持统一的接口。
主要概念
当经典的 Stellar 资产被移植到 Soroban 时,它由一个 SAC 表示 - 一个智能合约,为资产管理提供面向用户和管理的功能。SAC 公开用于处理同质化代币的标准函数,例如 transfer
、approve
、burn
等。此外,它们还包括管理功能(mint
、clawback
、set_admin
、set_authorized
),这些功能最初仅限于发行者(G 账户)。
set_admin
函数能够将管理控制权转移到自定义合约,从而允许更复杂的授权逻辑。这种灵活性为实现自定义规则开辟了可能性,例如基于角色的访问控制、两步管理转移、mint 速率限制和可升级性。
通用方法
SAC Admin 实现的通用方法:
-
利用
__check_auth
函数来处理身份验证和授权逻辑 -
为面向用户和管理功能保持统一的接口
-
允许注入任何自定义授权逻辑
-
需要更复杂的授权机制
示例实现
这是一个简化的 SAC Admin Generic 合约示例:
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum SACAdminGenericError {
Unauthorized = 1,
InvalidContext = 2,
MintingLimitExceeded = 3,
}
#[contracttype]
#[derive(Clone)]
pub struct Signature {
pub public_key: BytesN<32>,
pub signature: BytesN<64>,
}
#[contracttype]
pub enum SacDataKey {
Chief,
Operator(BytesN<32>), // -> true/false
MintingLimit(BytesN<32>), // -> (max_limit, curr)
}
#[contract]
pub struct SacAdminExampleContract;
#[contractimpl]
impl SacAdminExampleContract {
pub fn __constructor(e: Env, sac: Address, chief: BytesN<32>, operator: BytesN<32>) {
set_sac_address(&e, &sac);
e.storage().instance().set(&SacDataKey::Chief, &chief);
e.storage().instance().set(&SacDataKey::Operator(operator.clone()), &true);
e.storage()
.instance()
.set(&SacDataKey::MintingLimit(operator), &(1_000_000_000i128, 0i128));
}
pub fn get_sac_address(e: &Env) -> Address {
get_sac_address(e)
}
}
自定义授权逻辑
Generic 方法的关键特性是在 __check_auth
函数中实现自定义授权逻辑的能力:
use soroban_sdk::{
auth::{Context, CustomAccountInterface},
contract, contracterror, contractimpl, contracttype,
crypto::Hash,
Address, BytesN, Env, IntoVal, Val, Vec,
};
#[contractimpl]
impl CustomAccountInterface for SacAdminExampleContract {
type Error = SACAdminGenericError;
type Signature = Signature;
fn __check_auth(
e: Env,
payload: Hash<32>,
signature: Self::Signature,
auth_context: Vec<Context>,
) -> Result<(), SACAdminGenericError> {
// authenticate
// 验证
e.crypto().ed25519_verify(
&signature.public_key,
&payload.clone().into(),
&signature.signature,
);
let caller = signature.public_key.clone();
// extract from context and check required permissions for every function
// 从上下文中提取并检查每个函数所需的权限
for ctx in auth_context.iter() {
let context = match ctx {
Context::Contract(c) => c,
_ => return Err(SACAdminGenericError::InvalidContext),
};
match extract_sac_contract_context(&e, &context) {
SacFn::Mint(amount) => {
// ensure caller has required permissions
// 确保调用者具有所需的权限
ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?;
// ensure operator has minting limit
// 确保 operator 具有 minting 限制
ensure_minting_limit(&e, &caller, amount)?;
}
SacFn::Clawback(_amount) => {
// ensure caller has required permissions
// 确保调用者具有所需的权限
ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?;
}
SacFn::SetAuthorized(_) => {
// ensure caller has required permissions
// 确保调用者具有所需的权限
ensure_caller_operator(&e, &SacDataKey::Operator(caller.clone()))?;
}
SacFn::SetAdmin => {
// ensure caller has required permissions
// 确保调用者具有所需的权限
ensure_caller_chief(&e, &caller, &SacDataKey::Chief)?;
}
SacFn::Unknown => {
// ensure only chief can call other functions
// 确保只有 chief 才能调用其他函数
ensure_caller_chief(&e, &caller, &SacDataKey::Chief)?
}
}
}
Ok(())
}
}
// Helper functions
// 辅助函数
fn ensure_caller_chief<K: IntoVal<Env, Val>>(
e: &Env,
caller: &BytesN<32>,
key: &K,
) -> Result<(), SACAdminGenericError> {
let operator: BytesN<32> = e.storage().instance().get(key).expect("chief or operator not set");
if *caller != operator {
return Err(SACAdminGenericError::Unauthorized);
}
Ok(())
}
fn ensure_caller_operator<K: IntoVal<Env, Val>>(
e: &Env,
key: &K,
) -> Result<(), SACAdminGenericError> {
match e.storage().instance().get::<_, bool>(key) {
Some(is_op) if is_op => Ok(()),
_ => Err(SACAdminGenericError::Unauthorized),
}
}
完整示例
一个完整的示例实现可以在 sac-admin-generic 示例中找到。