Пост
Поделитесь своими знаниями.
Лучшие практики внедрения графиков передачи прав собственности в Sui
Я разрабатываю систему передачи токенов (например, для распределения ресурсов между командами или блокирования инвесторов) в Sui Move, и мне необходимо обеспечить следующее:
Безопасность — отсутствие преждевременного вывода средств
Гибкость — опора на скалы, линейная или постепенная укладка
Эффективность использования газа — минимальные затраты на хранение
Прозрачность — простая проверка статуса наделения
Текущие проблемы:
- Базовые таймеры работают, но не имеют детального управления
- Проблемы с управлением несколькими получателями
- Не знаете, как обращаться с случаями досрочного увольнения
Вопросы:
Какая архитектура наиболее безопасна для передачи прав на контракты?
Как реализовать различные кривые передачи прав собственности (линейные, ступенчатые, скальные)?
Как лучше всего работать с отзываемыми и безотзывными графиками?
Распространенные уязвимости, которых следует избегать?
- Sui
- Move
Ответы
1####1. Базовая архитектура тестирования
######Минимальная жизнеспособная реализация
module vesting::linear {
use sui::coin;
use sui::clock;
use sui::balance;
use sui::tx_context;
struct VestingSchedule has key {
id: UID,
recipient: address,
total_amount: u64,
start_time: u64, // Clock timestamp
duration_ms: u64, // Total vesting period
claimed: u64
}
/// Initialize vesting
public entry fn create(
recipient: address,
total_amount: u64,
duration_ms: u64,
clock: &Clock,
ctx: &mut TxContext
) {
let schedule = VestingSchedule {
id: object::new(ctx),
recipient,
total_amount,
start_time: clock.timestamp_ms,
duration_ms,
claimed: 0
};
transfer::transfer(schedule, recipient);
}
}
Ключевые компоненты:
Clock
на основе временных меток (а не номеров блоков)- Самостоятельно настраиваемые объекты расписания
- По умолчанию линейное наделение
####2. Продвинутые паттерны наделения
######Клифф + Линейное жилет
public fun claimable_amount(
schedule: &VestingSchedule,
clock: &Clock
): u64 {
let elapsed = clock.timestamp_ms.saturating_sub(schedule.start_time);
// Cliff period (e.g., 1 year)
if (elapsed < CLIFF_MS) return 0;
// Linear vesting after cliff
let vested = (schedule.total_amount * elapsed) / schedule.duration_ms;
vested - schedule.claimed
}
#####Поэтапное распределение (несколько этапов)
struct GradedVesting has key {
id: UID,
milestones: vector<Milestone>, // [{time: u64, percent: u8}]
claimed: u64
}
public fun graded_claimable(schedule: &GradedVesting, clock: &Clock): u64 {
let total_vested = 0;
let i = 0;
while (i < vector::length(&schedule.milestones)) {
let milestone = vector::borrow(&schedule.milestones, i);
if (clock.timestamp_ms >= milestone.time) {
total_vested = total_vested +
(schedule.total_amount * milestone.percent as u64) / 100;
};
i = i + 1;
};
total_vested - schedule.claimed
}
####3. Усиления безопасности
######Отзывное наделение (административный контроль)
struct AdminCap has key { id: UID }
public entry fn revoke(
schedule: VestingSchedule,
admin_cap: &AdminCap,
ctx: &mut TxContext
) {
assert!(address_of(signer) == admin_cap.admin, EUnauthorized);
let remaining = schedule.total_amount - schedule.claimed;
coin::transfer(remaining, admin_cap.admin, ctx);
object::delete(schedule);
}
#####Антифронтальный запуск
public entry fn claim(
schedule: &mut VestingSchedule,
clock: &Clock,
ctx: &mut TxContext
) {
let amount = claimable_amount(schedule, clock);
assert!(amount > 0, ENothingToClaim);
assert!(tx_context::epoch(ctx) == clock.epoch(), EStaleClock);
schedule.claimed = schedule.claimed + amount;
transfer::public_transfer(coin::mint(amount, ctx), schedule.recipient);
}
####4. Методы оптимизации газа
######Графики пакетной сдачи
struct BatchVesting has key {
id: UID,
schedules: Table<address, VestingSchedule>
}
// Claim all eligible in one TXN
public entry fn batch_claim(batch: &mut BatchVesting, clock: &Clock) {
let iter = table::iter(&mut batch.schedules);
while (table::has_next(&iter)) {
let (addr, schedule) = table::next(&iter);
let amount = claimable_amount(schedule, clock);
if (amount > 0) transfer_coins(addr, amount);
}
}
#####Скидки на хранение
// Use small types where possible
struct CompactVesting {
start_time: u64,
duration_ms: u32, // Sufficient for 49-day periods
claimed: u64
}
####5. Распространенные уязвимости
❌Атаки Oracle со временем
// UNSAFE: Trusting user-provided timestamps
fun unsafe_claim(user_time: u64) { ... }
❌Ошибки округления
// May leave "dust" unclaimed
let vested = (amount * elapsed) / duration; // Use checked_math
❌Отсутствует логика отзыва
// Irrevocable vesting may violate securities laws
struct DangerousVesting { ... }
####6. Стратегии тестирования
#####Тестирование макетов часов
#[test]
fun test_cliff_period() {
let clock = mock_clock(0);
let schedule = create_vesting(CLIFF_MS * 2);
// Before cliff
test_clock::advance(&mut clock, CLIFF_MS - 1);
assert!(claimable(&schedule, &clock) == 0, ETestFail);
// After cliff
test_clock::advance(&mut clock, 1);
assert!(claimable(...) > 0, ETestFail);
}
#####Тестирование нечеткий
#[test]
fun test_overflow_scenarios() {
let schedule = VestingSchedule {
start_time: u64::MAX - 1000,
duration_ms: 2000
};
// Should handle wrap-around correctly
claimable_amount(&schedule, &clock);
}
Знаете ответ?
Пожалуйста, войдите в систему и поделитесь им.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Заработай свою долю из 1000 Sui
Зарабатывай очки репутации и получай награды за помощь в развитии сообщества Sui.
- Почему BCS требует точного порядка полей для десериализации, когда структуры Move содержат именованные поля?53
- «Ошибки проверки нескольких источников» в публикациях модуля Sui Move — автоматическое устранение ошибок43
- Сбой транзакции Sui: объекты, зарезервированные для другой транзакции25
- Как ограничения возможностей взаимодействуют с динамическими полями в гетерогенных коллекциях?05